MySQL/JPA : How create a correclty relation and cascade? - mysql

I was very impress with my project, but suddenly I notice that I am doing something very wrong, 'cause, every create, merge, delete I was doing manually, but I should let the JPA take care of that for me right ?
What I do, is create the database then create the entities from it. So here is my database so you guys can understand my concept issue.
In a simple way:
An user has an address, only one address. This address is composed by city, state and country. I want to be able to create, update the user address by cascade.
I think my mapping, generated by JPA is wrong (or my understaing is wrong), let me show it:
public class User {
..
//bi-directional many-to-one association to UserAddress
#ManyToOne(cascade = CascadeType.ALL) // should be one to one ?
#JoinColumn(name="id_user_address")
private UserAddress userAddress;
..
}
public class UserAddress {
..
//bi-directional many-to-one association to User
#OneToMany(mappedBy="userAddress", fetch=FetchType.EAGER)
private List<User> users;
//bi-directional many-to-one association to AddressCity
#ManyToOne
#JoinColumn(name="id_city")
private AddressCity addressCity;
//bi-directional many-to-one association to AddressState
#ManyToOne
#JoinColumn(name="id_state")
private AddressState addressState;
//bi-directional many-to-one association to AddressCountry
#ManyToOne
#JoinColumn(name="id_country")
private AddressCountry addressCountry;
..
}
What I think is that the user has one adress only, so it should be OneToOne mapping ?
And the same goes for UserAdress about country, state and city.

If you want to allow multiple users on the same address #ManyToOne is what you want. If you use #OneToOne you can have only one user per address.

Related

How to store value objects in relational database like mysql

I have a scenario where I have the user table and the address table. The address table is a value objects in domain driven design in my understanding. How do I store value objects in mysql database? this sounds a bit confusing but I couldn't understand this idea value objects are immutable but how to store them?
Below are classes of my two entity
user.java
#Getter #Setter #NoArgsConstructor
#Entity // This tells Hibernate to make a table out of this class
#Table(name="user")
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#JsonProperty("userid")
#Column(name="userid")
private Long user_id;
#JsonProperty("user_nome")
private String nome;
#JsonProperty("user_email")
#Column(unique = true, nullable = false)
private String email;
#JsonProperty("user_cpf")
private String cpf;
#JsonProperty("user_telefone")
private String telefone;
#JsonProperty("user_celular")
private String celular;
#JsonProperty("user_senha")
private String senha;
#Column(name="createdAt", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
#Temporal(TemporalType.TIMESTAMP)
#JsonProperty("user_createdAt")
private Date createdAt;
#Column(name="updateAt", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
#Temporal(TemporalType.TIMESTAMP)
#JsonProperty("user_updateAt")
private Date updateAt;
/*Person p1 = new Person("Tom", "Smith");
p1.setId(1L);
p1.setStartDate(new Date(System.currentTimeMillis())); */
}
class Address:
#Getter #Setter #NoArgsConstructor
#Entity // This tells Hibernate to make a table out of this class
#Table(name="address")
public class Address {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#JsonProperty("address_id")
private Long address_id;
#JsonProperty("address_endereco")
private String endereco;
#JsonProperty("address_bairro")
private String bairro;
#JsonProperty("address_numero")
private String numero;
#JsonProperty("address_complemento")
private String complemento;
#JsonProperty("address_cidade")
private String cidade;
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "userid")
private User userid;
}
Basically however you want: you could enforce immutability in the database, but you don't have to. Immutability can be enforced in the database by creating an unique constraint on a combination of values of an address, zipcode + house number for example.
As a database administrator I personally don't like developers enforcing immutability in the database because I see implementing/enforcing business logic in the database as a category error. What is an immutable value within the domain, to me is just data that needs to be consistently stored. Database rules are meant to ensure data consistency and the added complexity of implementing immutability in the database can interfere with that. Lets do a thought experiment:
You ensure that an address value is unique in the database with a constraint that covers all properties and store your data. Some time later a customer places an order that happens to have the same address, but he lives on the North Pole. The order is saved but the address isn't because my server throws an error because the address violates the constraint because it already exsists in the US, but that's not saved/part of the constraint. Now I have a problem because that orphaned order violates the data model, you complain to me because my server threw an error and now it's up to me to figure out what's wrong with your design decision to apply the abstract concept of immutability outside your domain, and have to update the data definition in a production environment in order to fix it.
So I think it's best you acknowledge that by storing data it leaves your domain and that is a risk your design should take into account. What I'd advice (or silently implement haha) would be the addition of an ID within the table and a record versions of the same 'immutable value' for tracability, consistency and agility to react to unforseen circumstances. Just like with user and transaction entities ;)

mappedBy issue with Ebean/Play framework

I try to create a link between two tables : User and Notifications
The Notifications tables should have:
Id (int)
User_Id (int)
List < User >
I need to clarify my goal. In my app, a user ask something to several others, using notifications tables. So that, we can know:
Who is the user who asks the question (User_Id)
To whow users the question is aked (List< User >)
User
#Entity
public class User extends Model{
#ManyToOne
#JoinColumn(name="notification_fk")
public Notifications notification;
}
Notifications
#Entity
public class Notifications extends Model{
#Id
#GeneratedValue
public int id;
public User user;
#OneToMany(cascade=CascadeType.ALL, mappedBy="notifications")
public List<User> asked_users = new ArrayList<User>();
}
But I get the following error:
javax.persistence.PersistenceException: Error on
models.Notifications.asked_users Can not find mappedBy property
[user] in [models.User]
What did I do wrong?
In the mappedBy you need to use the name of the existing oposite field, and it's notification in your case - without 's' at the end.

How to persist but avoid unique keys exceptions?

My problem is simple:
A user has an address, address is composed of city, state and country.
So for that I have this structure:
public class User {
..
//bi-directional many-to-one association to UserAddress
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name="id_user_address")
private UserAddress userAddress;
..
}
Note that CascadeType does not have CascadeType.ALL because if the user is deleted it won't affect the cities, states and countries created.
public class UserAddress {
..
//bi-directional many-to-one association to AddressCity
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.})
#JoinColumn(name="id_city")
private AddressCity addressCity;
//bi-directional many-to-one association to AddressCountry
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name="id_country")
private AddressCountry addressCountry;
//bi-directional many-to-one association to AddressState
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name="id_state")
private AddressState addressState;
..
}
My problem is if I try to register a user that has the same city for example it gives me an exception:
SEVERE: Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'Campinas' for key 'city_UNIQUE'
How can I avoid this kind of constraint, keeping the CASCADE?
And anyone know how to show the queries made by JPA in my JSF project?
The CascadeType.PERSIST annotation for User.userAddress causes userAddress to be persisted for each User persisted and CascadeType.PERSIST annotation for UserAddress.addressCity causes addressCity to be persisted for each UserAddress persisted. Thus when you persist a user with same city, new city is tried to be persisted with the same key which causes constraint violation. So remove the cascades for those ManyToOne annotated relationships.
You have to check on saving User whether userAddress already exists and check on saving UserAddress whether the relationships inside it already exist and take required action.

UNIQUE KEY generated from nowhere

I have 2 tables with #ManyToMany relation field. In hibernate cfg i have
<property name="hbm2ddl.auto">update</property>
Table which is created during application startup has UNIQUE key set on PartId column, which is
#JoinColumn(name="PartId")}
in #ManyToMany relation. I didn't set anywhere that this column should have unique key. Is this the default auto creation behaviour?
The DB is MySQL 5.5
Thanks.
UPD:
Full field desc is:
#ManyToMany
#JoinTable(name="Part_Dev",
joinColumns={#JoinColumn(name="PartId")},
inverseJoinColumns= {#JoinColumn(name="DevCode")})
public List<Dom> getDom() { return dom; }
UPD 2
sorry, I see I didn't mention it. Unique key in Parts table,
#Entity #Table(name="Parts")
public class Parts implements Serializable{
#ManyToMany
#JoinTable(name="Part_Dev",
joinColumns={#JoinColumn(name="PartId")},
inverseJoinColumns= {#JoinColumn(name="DevCode")})
public List<Dom> getDom() {
return dom; }
#Column(name="PartId")
public Integer getPartId() {
return partId; }
you need to specify #JoinTable to make it happen. For instance, if you have two entities : Employee and Project in a many-to-many relationship. You need to have #JoinTable on one side.
#Entity
public class Employee {
#Id private int id;
private String name;
#ManyToMany
#JoinTable(name="EMP_PROJ",
joinColumns=#JoinColumn(name="EMP_ID"),
inverseJoinColumns=#JoinColumn(name="PROJ_ID"))
private Collection<Project> projects;
So, as Chris told, that was the way to identify each part.

Simplest one-to-many Map case in Hibernate doesn't work in MySQL

I think this is pretty much the simplest case for mapping a Map (that is, an associative array) of entities.
#Entity
#AccessType("field")
class Member {
#Id
protected long id;
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.LAZY)
#MapKey(name = "name")
private Map<String, Preferences> preferences
= new HashMap<String, Preferences>();
}
#Entity
#AccessType("field")
class Preferences {
#ManyToOne Member member;
#Column String name;
#Column String value;
}
This looks like it should work, and it does, in HSQL. In MySQL, there are two problems:
First, it insists that there be a table called Members_Preferences, as if this were a many-to-many relationship.
Second, it just doesn't work: since it never populates Members_Preferences, it never retrieves the Preferences.
[My theory is, since I only use HSQL in memory-mode, it automatically creates Members_Preferences and never really has to retrieve the preferences map. In any case, either Hibernate has a huge bug in it or I'm doing something wrong.]
And of course, I sweat the problem for hours, post it here, and a minute later...
Anyway, the answer is the mappedBy element of the #OneToMany annotation:
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="member")
#MapKey(name = "name")
private Map<String, Preferences> preferences
= new HashMap<String, Preferences>();
Which makes a certain sense: which field in the Many entity points back to the One entity? Even allowing that looking for a matching #ManyToOne field was too error prone, I think that what they did do (assuming the existence of a mapping table) makes even worse.