I have MySql database with several tables. Every field in each table is not nullable. That's why I am forced to use #NotNull annotation to each field in all my Java classes marked with #Entity. Do I really have to do this or is there a way to tell JPA/Hibernate to treat every field NotNullable on default?
edit:
I am aware of #Column(name="something", nullable=false) too. But still, it doesn't solve any problem - you have to write nullable=false
There are no such possibility. Not Nullable constraint is not what you always expect from a field, although it is used quite often. It is convenient when you can look at the attribute definition and tell everything out of it, without addressing to some high-level settings like "every field should be #NotNull".
It would be rather confusing to see such entity definition with this setting hidden elsewhere.
And, one more thing. #NotNull annotation and #Column(name="something", nullable=false) are not the same. More details here :
Confusion: #NotNull vs #Column(nullable = false)
Related
I had a little misunderstanding. Let's say I have such an entity:
#Entity
public class Item {
private String description;
}
As well as DTO to this entity:
public class ItemDto {
private String description;
}
As you already understand, all controllers work only with the DTO, that is, I get the DTO from the client part, and then convert it into an entity, and so on.So, I decided to validate the DTO:
public class ItemDto {
#Max(value = 100, message = "Description must not exceed 100 characters")
private String description;
}
Validation works well, but I have a question: do I need to validate entities as well? I always thought that annotations for validation in entity are useless because you still create the necessary tables yourself and set the necessary restrictions. But then the question arises, should entities correspond to the table in the database at all? That is, for example, if I have a NOT NULL constraint in the table for a field, should I also put the #NotNull annotation over this field in the entity? The DTO is also not clear. I'm validating the DTO, but this probably doesn't guarantee complete data protection.in other words, in theory, some programmer might decide to do something directly with the entity, but it isn't validated. All this will lead to an error. Or there may be a situation where restrictions are set in the table in the database, but there are no restrictions in the entity.in other words, you can assign any value to the entity, but an error occurs when adding it to the database. Help me deal with the situation. I'm sorry if I didn't explain it very well.
You said
Should entities correspond to the table in the database at all? That
is, for example, if I have a NOT NULL constraint in the table for a
field, should I also put the #NotNull annotation over this field in
the entity?
Bean Validation's purpose is to be used for validating beans. So, for cases where you are not validating the entity, there is clearly no point of placing an unused annotation.
You said
I'm validating the DTO, but this probably doesn't guarantee complete.
data protection
JPA annotations serve this purpose. For example, in the NOT NULL scenario you mentioned, you can use #Column as:
#Column(nullable = false)
Am i right that #Id annotation add two constraints in database:
nullable=false
unique=true
?
I saw a lot of examples in the Internet with syntax like
#Id
#Column(name="xxx",nullable=false)
BigInteger id
It is correct? Do i really need this nullable=false?
Yes you are right. If you use hibernate schema generation mechanism, all #Id columns in the database will be NOT NULL and have unique index by default.
In the other hand, #Column(nullable=false) declaration is absolutely meaningless if you create the schema any other way.
One reason you might see the two together is for the name attribute on #Column. It's name attribute lets you explicitly choose the name of the column in the resulting table, in cases where the default name that JPA provides. I will at times use #Column solely for that purpose, just so I can give my column a certain name.
As for the nullable attribute, I agree with you. It's worthless in that case.
From #Column annotation documentation i verified nullable attribute has true as default value.
In my entities definitions I'd like to set columns non-nullable as default behavior, but I don't want to set this for every single column.
Is there a way to globally change default value for nullable attribute (and eventually others)?
JPA takes the nullable property from the tables on your database if the columns on your table were notnull then your entity attributes would be #NotNull too. You could do the changes on your database and recreate the entities.
I'm not familiar with a way to override JPA default settings, but you could use EntityListeners to perform some #PrePersist validations and check that an object fields (non transient ones) are not null by reflection.
Having said that, I believe that this makes the entity definition less clear and would rather stick to the more declarative technique using (nullable="false").
I'm quite new to JPA and Hibernate (I'm studying hard though!) and I am struggling with a problem that I can't seem to find a trivial solution for, so here it is.
I have an entity that looks kinda like the following:
#Entity
#Table(name = "mytable1")
public class EntityOne {
// surrogate key, database generated
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
// business key
#Column(name = "identifier", nullable = false, unique = true)
private String identifier;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name = "twoId", nullable = false)
private EntityTwo two;
#OneToMany(mappedBy = "entityOne", fetch = FetchType.EAGER,
cascade = {CascadeType.ALL}, orphanRemoval = true)
private Set<EntityThree> resources = new HashSet<>();
// getters/setters omitted
#Override
public int hashCode() {
// the business key should always be defined (through constructor/query)
// if this is null the class violates the general hashcode contract
// that the integer value returned must always be the same
Assert.notNull(identifier);
// a dirty alternative would be:
// if(identifier==null) return 0;
return identifier.hashCode();
}
#Override
public boolean equals(Object o) {
return o instanceof ResourceGroup
&& ((ResourceGroup) o).identifier.equals(identifier);
}
}
My project is set up with Spring JPA, so I have my CrudRepository<EntityOne,Long> injected in a Service class that has a few #Transactional methods and I scan my domain/service packages for JPA and transactions respectively.
One of the service methods calls the repository's findAll() method and returns a list of EntityOnes. Everything works fine unless I try to access the getter for two, which obviously throws:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
I thought it might be useful to have this object initialized, so I switched the fetching type from lazy to eager. However, if I do that I get the following:
java.lang.IllegalArgumentException: [Assertion failed] - this argument is required; it must not be null
at org.springframework.util.Assert.notNull(Assert.java:112)
at org.springframework.util.Assert.notNull(Assert.java:123)
at my.pkg.domain.EntityOne.hashCode(ResourceGroup.java:74)
at java.util.HashMap.hash(HashMap.java:351)
at java.util.HashMap.put(HashMap.java:471)
at java.util.HashSet.add(HashSet.java:217)
at java.util.AbstractCollection.addAll(AbstractCollection.java:334)
at org.hibernate.collection.internal.PersistentSet.endRead(PersistentSet.java:346)
at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:243)
at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:233)
at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:209)
at org.hibernate.loader.Loader.endCollectionLoad(Loader.java:1149)
//...
I briefly looked at Hibernate's source code and it looks like it's trying to put my EntityOne objects in a set before their business key is initialized. Is my interpretation correct? Is there a way around this? Am I doing something incredibly dumb?
I appreciate your help
EDIT: I just want to clarify that what I'm trying to understand here is what the best practices are specifically with respect to JPA and Hibernate. If this was a plain POJO I could make the identifier field final (I would actually make the whole class immutable) and be safe. I can't do this because I'm using JPA. So the questions: do you violate the hashCode contract and in which way? How does Hibernate deal with this violation? What's the JPA recommended way of doing this in general? Should I get rid of hash based collections altogether and use lists instead?
Giovanni
No, you're not doing anything dumb. Implementing equals and hashCode on a JPA entity is a matter of much heated debate, and all of the approaches I know about have significant drawbacks. There's no obvious, trivial solution that you're just missing.
You have, however, hit on a case which is not discussed very much for some reason. The hibernate wiki recommends using a business key as you are doing, and "Java Persistence with Hibernate" (Bauer / King, 2007, widely regarded as the standard Hibernate reference work) on page 398 recommends the same thing. But in some situations, as you observe, Hibernate can add an entity into a Set before its fields are initialized, so the business-key-based hashCode doesn't work, just as you point out. See Hibernate issue HHH-3799 for discussion of this case. There is an expected-to-fail test case in the Hibernate source code demonstrating the issue, added in 2010, so at least one Hibernate developer considers it to be a bug and wants to fix it, but there hasn't been any activity since 2010. Please consider voting for that issue.
One solution you might consider is to expand the scope of your session so that all your access to entities happens within the same session. Then you can make your Set<EntityThree> be lazy-fetched instead of eager-fetched, and you'll avoid the eager-fetching problem in HHH-3799. Most applications I've worked on make only sparing use of objects in the detached state. It sounds like you're loading your entity and then using it for a while after the session ends; that's a pattern I'd recommend against. If you're writing a web application, see the "open session in view" pattern and Spring's OpenSessionInViewFilter for ideas on how to do this.
Incidentally, I like how you throw an exception when the business key is not initialized; that way you can catch coding errors quickly. Our application has a nasty bug due to HHH-3799 which we might have caught in development if we had used your not-null assertion.
Your interpretation is correct. As a first first step code your hashCode() and equals() with your id field - the one you are telling Hibernate that is your id.
As a second step implement a correct hashCode() and equals() to save you from future trouble. There are plenty of resources if you google it. Here is one on this site
I believe I actually found a way to make this work a little better, i.e., forcing Hibernate (or whatever JPA provider) to have the key available before sticking objects in the collection. In this scenario, the object will be properly initialized and we can be sure that the business key won't be null.
For example, here's how the class EntityTwo would have to look:
#Entity
#Table(name = "mytable2")
public class EntityTwo {
// other code omitted ...
#OneToMany(mappedBy = "entityTwo", fetch = FetchType.EAGER,
cascade = {CascadeType.ALL}, orphanRemoval = true)
#MapKey(name = "identifier")
private Map<String, EntityOne> entityOnes = new HashMap<>();
}
I haven't tested this specific code but I have other working examples and it should work fine according to the JPA docs. In this case, the JPA provider is cornered: it must know the value of identifier before it can put the object in the collection. Besides, the object's hashCode and equals are not even called because the mapping is explicitly handled by the JPA provider.
This is a case in which explicitly forcing the tool to understand the way things are modeled and related to each other leads to great benefit.
In JPA, I am confused when to use the attribute optional=false and the annotation #Column(nullable=false). What is the difference?
#Column(nullable=false) is an instruction for generating the schema. The database column generated off the class will be marked not nullable in the actual database.
optional=false is a runtime instruction. The primary functional thing it does is related to Lazy Loading. You can't lazy load a non-collection mapped entity unless you remember to set optional=false (because Hibernate doesn't know if there should be a proxy there or a null, unless you tell it nulls are impossible, so it can generate a proxy.)
Both is used to prevent a null value, but if you mind that null should be blocked in ...
The database layer (and you want to generate the schema using JPA) --> use #Column(nullable=false)
The runtime (and before contacting the database)--> use optional=false (much faster than the first checking).
If you want both abilities, use them both.