Lets say I'm writing a JPA entity. Very simple one. It has 3 properties:
public class MenuItem {
#Id
#GeneratedValue
private Long menuItemId;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private Type type;
#OneToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "colorId")
private Color color;
}
Now, lets say I need to override its equals/hashcode methods. According to this suggestion Implementing equals() and hashCode() I have to use some sort of "Business key equality". But I have doubts, what should I consider as a "Business" key here. Does it have to be all the properties except of ID, or Color can be excluded from it. Just seems unclear to me. Would be thankful if anyone can explain more on this topic.
The last statement in the reference is pure gold:
Note that a business key does not have to be as solid as a database
primary key candidate. Immutable or unique properties are usually good
candidates for a business key.
So in your case name and type would be good candidates. Assuming the Type would be an immutable object (you could still get away without that but you might experience some hard to detect bugs).
IF you would put a unique constraint on color then i would consider that in the equals / hashCode method also.
Related
After reading several articles, threads and making some research, now I am completely confused regarding to implementing a proper equals and hashCode method in my Spring Boot app.
For example, I have the following class:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String title;
#Column(length = 100)
private String description;
#Column(nullable = false)
private Integer prepTime;
#Column(nullable = false)
private Integer cookTime;
#Column(nullable = false)
private Integer servings;
#Lob
#org.hibernate.annotations.Type(type = "org.hibernate.type.TextType")
#Column(nullable = false)
private String instructions;
#Column(nullable = false)
#Enumerated(value = EnumType.STRING)
private Difficulty difficulty;
#Column(nullable = false)
#Enumerated(value = EnumType.STRING)
private HealthLabel healthLabel;
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name = "category_id", referencedColumnName = "id")
private Category category;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RecipeIngredient> recipeIngredients = new ArrayList<>();
}
I have trouble for these issues below and trying to implement equals and hashCode methods properly:
1) As far as I know, when there is a unique field that can be used to differentiate one person from the other e.g. email or username, then it is enough ONLY use these fields in equals and hashCode method. Is that true?
2) If there is not any unique field except from id, then should I add ALL the fields (id and the others) to equals and hashCode method implementation?
3) When using Hibernate and Data JPA, should I follow a different approach than other situation as there are 4 states transient, managed, removed, and detached in JPA lifecycle? I think id field should not be used in this situation as it is not present in transient mode? Right?
When implementing equals() and hashCode() methods:
If there is a unique field that can be used to differentiate one object from another, use only that field in the implementation.
If there is no unique field, use all the fields including the id in the implementation.
When using Hibernate and Data JPA, do not use the ID field in the implementation as it is not present in the transient state, instead use fields that are present in all states such as unique fields or all fields.
The problem with equals and hashCode is that their contract is broken for any mutable entity and with JPA, there aren't really any other.
Ignoring JPA for a moment, by definition the id of an entity defines its identity.
So it should be used for equals and hashCode.
But this requires the id to be unmodifiable in an entity, but JPA requires a no args constructor and a way to set all properties, including the id.
Probably the best way around this is to
use the id.
make sure that equals and hashCode is never ever used before the id is set, and the id is never changed afterwards.
Not changing the id after it is once set is normally not a problem, since the id shouldn't change from one value to another.
The problem is creation of new instances.
Again instances returned by JPA aren't a problem, because JPA will fully initialise them before returning them to you.
Creating fresh instances in your application is the problem.
Here you have the following options:
create the instance and immediately assign a id. UUIDs are perfect for this.
They can be generated easily and efficiently on the application server.
This could be done in a static factory method on the entity class.
The drawback is that UUIDs are a pain to work with for humans, since they are long and basically random.
They are also large and eat more memory in the database than a traditional sequence number.
But the use cases with so many rows that this actually is a problem are rare.
generate the id in the database as most people do, and make sure that your new entity gets saved immediately after creation.
This could be nicely done in a custom method in a repository.
But it does require that you set all required properties in one place, which often can be a problem.
Using some other attribute which is supposed to be immutable, like the account name or an email works only for very few entities in the first place and even for those the fact that it is immutable now doesn't mean it stays that way.
Instead of trying to avoid the pitfalls created by JPA you could alternatively rely on it.
JPA guarantees that for a given class and id only one instance is in a persistence context.
Therefore, as long as you only work within a single session/transaction with an entity and don't try to compare detachd entities, there is no need to implement equals and hashCode at all.
as you are already using lombok, you can use #Data annotation as well:
#Data
All together now: A shortcut for #ToString, #EqualsAndHashCode, #Getter on all fields, #Setter on all non-final fields, and #RequiredArgsConstructor!
I'm new in Hibernate. What is the difference between
#Id
#GeneratedValue
private Integer id;
and
#Id
#GeneratedValue
#Column(name="id", unique=true, nullable=false, etc)
private Integer id;
What is the reason of defining this if we already did it creating database? Does Hibernate warn us when we break these constraints (not in this example since it's auto generated value)? Does it create table when there isn't one?
The only reason is if the column is named differently, for example in a USER table, if the id column is called USER_ID, you might want to map that to a User object, on the field id.
The unique and nullable attributes are not used, as PK are unique and non-null. Read the javadoc of #Column to understand what else you can define there.
As you mentioned, if you create your DB from scripts (it's actually a bad practice to create your production DB from the JPA annotations), the only relevant attribute is the name... the other ones are not used for #Id columns or are used to autogenerate the DDL.
A slight word of caution, sometimes the attribute nullable influences on how updates are ordered. As a good practice, I always set it to false on mandatory associations (as otherwise Hibernate might decide clear the association and set the column to null... and then do another update with the new ID.).
I am new to both stackoverflow and JPA so I will try to explain this the best i can.
In an entity I want to set the foreign key by giving the int value but also I want to set it by giving an object. Here is some code to explain it better.
#Entity
public class Thread implements Serializable {
#ManyToOne
#JoinColumn(name = "accountId", referencedColumnName = "id", nullable = false)
public Account getAccount() {
return account;
}
#Column(name = "accountId")
#Basic
public int getAccountId() {
return accountId;
}
}
I have tried several ways but the code above is the best example for what I am trying to achieve. I understand that setting insert = false and update = false, in either of the 2 methods, makes this code work as far as compiling and running. But I want to be able to insert the accountId by using an Account object AND by setting the actual int accountId.
The reason for this is because sometimes, in my server, I only have the accountId and sometimes I have the Account object.
I also understand that the best solution is probably to use account.getId() when creating the Thread and setting the accountId. But it would be logically nice in my server to be able to just use the object.
Thanks in advance!
I think you have hit a conceptual problem in your application. You should stick to set the entity and do not use any foreign key values when using JPA. The cause of the problem is that your application is only providing the accountId at some point.
This may be due to different reasons. If this is because the part of the application only providing the accountId is legacy, than I would think it is perfectly fine to have an adapter that converts the accountId into an Account entity and then set that entity. Also not that the adapter could create a JPA proxy so that no actual database access is required at that point. Another reason I can think of, is that the application is loosing information at some point during processing. This may be the case when the application is using the Account in some place and only hands over it's Id to the code in question. Then such code should be refactored to hand over the entity.
In your specific case you are also able to use both, account as entity and the foreign key as attribute with both being insertable and updatable. You just have to make sure, that the accountId attribute value is consistent with the foreign key pointing to the row represented by the account entity. JPA providers should be able to handle this (I know OpenJPA does for example). However you are a bit restricted with this. For example you are only able to read the accountId attribute value, because setting it to a different value would cause an inconsistency between the account entity value.
I have seen this question at SO which tends to lead towards Primitives and also seen this one from coderanch which tends to lead towards wrappers. Both are slightly old too.
I do not have any special needs just want to know a standard good practice.
Examples on web are mixed too. e.g some with go like this:
#Id
#Column(name = "CUSTOMER_ID")
public long customerId;
Others with Wrappers:
#Id
#Column(name = "CUSTOMER_ID")
public Long customerId;
The difference between the two is nullability. the primitive type is unable to be null, while the "Wrapped" type can be null.
I prefer to use the wrapped type as you can tell if the object has been saved/loaded to/from the database whether or not the id value is null.
I don't think there is a "best practice" here, maybe a matter of style?
Hibernate recommends you:
We recommend that you declare consistently-named identifier properties
on persistent classes and that you use a nullable (i.e.,
non-primitive) type. more
I think that answer is included in nullable element in #Column annotation. If it can be nullable than wrapped primitive is ok. But on nullable=false columns ( as ID is) primitives are better. You will get extra checking because null cannot be cast to int/long.
If you use primitives it will always hold a default value, in this case 0L for long, even if the value is not there in the database. And if you use the wrapper object it will be having a null value if the value is not in the database or the entity is not persisted yet.
From an Hibernate point of view, it doesn't change anything as Hibernate uses the same Hibernate type to represent them.
However, as pointed out by Bytecode Ninja, you can't distinguish the default value of a primitive int 0 from a an assigned 0 while there is no possible ambiguity with a null (a null id always means a new entity), which is why I prefer to use a nullable wrapper type.
And this is the Hibernate recommendation. From the Reference Documentation:
4.1.2. Provide an identifier property (optional)
Cat has a property called id. This property maps to the primary key column of a database table. The property might have been called anything, and its type might have been any primitive type, any primitive "wrapper" type, java.lang.String or java.util.Date. If your legacy database table has composite keys, you can use a user-defined class with properties of these types (see the section on composite identifiers later in the chapter.)
The identifier property is strictly optional. You can leave them off and let Hibernate keep track of object identifiers internally. We do not recommend this, however.
In fact, some functionality is available only to classes that declare an identifier property:
Transitive reattachment for detached objects (cascade update or cascade merge) - see Section 10.11, “Transitive persistence”
Session.saveOrUpdate()
Session.merge()
We recommend that you declare consistently-named identifier properties on persistent classes and that you use a nullable (i.e., non-primitive) type.
And I actually leverage this in my base class:
#MappedSuperclass
public class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Transient
public boolean isNew() {
return (this.id == null);
}
}
Please check the more details here:https://stackoverflow.com/posts/3537407/edit
Create Table A (
ID varchar(8),
Primary Key(ID)
);
Create Table B (
ID varchar(8),
A_ID varchar(8),
Primary Key(ID),
Foreign Key(A_ID) References A(ID)
);
Given that I have created two tables using the SQL statements above, and I want to create Entity classes for them, for the class B, I have these member attributes:
#Id
#Column(name = "ID", nullable = false, length = 8)
private String id;
#JoinColumn(name = "A_ID", referencedColumnName = "ID", nullable = false)
#ManyToOne(optional = false)
private A AId;
In class A, do I need to reciprocate the many-to-one relationship?
#Id
#Column(name = "ID", nullable = false, length = 8)
private String id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "AId")
private List<B> BList; //<-- Is this attribute necessary?
Is it a necessary or a good idea to have a reciprocal #OneToMany for the #ManyToOne? If I make the design decision to leave out the #OneToMany annotated attribute now, would that come back to bite me further along?
Is it a necessary or a good idea to have a reciprocal #OneToMany for the #ManyToOne?
No, it's not mandatory at all, it's a pure design decision. The whole question is... Do you want this (i.e. an uni-directional association):
Or this (i.e. a bi-directional association):
If you don't need to get Bs from A, then you can skip the bs attribute and the OneToMany on A side.
If I make the design decision to leave out the #OneToMany annotated attribute now, will come back to bite me further down.
No, and you can add it later if you discover that you need it.
They are optional. There is no need to add them to your model if you don't want to use them.
I'd sugguest to avoid the reverse mapping at all because such collections may become quite large and most persistance layers don't handle these very good. In many cases you'd have to deal with add/remove of already loaded/managed entities related to these collections yourself. So only add those if they really make things easier for you.
Sure not. Thats design decision between one vs two direction relationship. In most casses, a better choice is to have one direction relationship, especially if its a domain classes. doing this, your design will express better the mean of your domain.