-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 13 posts ] 
Author Message
 Post subject: EntityManager.persist: ¿What does/should it mean?
PostPosted: Tue Mar 17, 2009 1:27 am 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
Hi!
Lets say you are presented with the following JPA @Entity:

Code:
@Entity
public class Customer {

private Long id;
private String name;

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

@Column(nullable=false)
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}



And then the following test

Code:
@Test
public void persistCustomerInTransaction() throws Exception {
factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);

EntityManager em = factory.createEntityManager();

em.getTransaction().begin();

Customer customer = new Customer();

em.persist(customer);em.getTransaction().commit();

em.close();

}


And the following question: Where does it crash?:

1. At the line em.persist(customer); because the name is configured as nullable=false, and we are trying to persist the Customer instance with a null value in the name property
2. em.getTransaction().commit(); because the name is configured as nullable=false, and we are trying to commit the transaction with a null value in the name property

Turns out… that the answer depends on the JPA provider you are using!!!! If using Hibernate, it crashes at em.persist, but if using EclipseLink, it crashes at em.getTransaction().commit.

Now, this might seem irrelevant, but it is in fact very important, because EclipseLink behavior means that constraint validations are deferred until the point where the transaction is committed, and that means that the developer has a lot more freedom to manipulate its persistent objects: they can be in any (possibly invalid) state while they are being manipulated by the business logic of the application, and they only have to be “right” the moment one needs to commit them to the database (not before), this is specially useful, for example, when building a wizard like UI (or a plain simple CRUD UI with support for full object graphs) with EclipseLink, I can "persist" my objects as soon as I want, and if want to, for example, run some validation logic at the end, all I have to do is ask the EclipseLink to give a list of all the objects that are going to be written in to the database, with Hibernate, the entityManager does not help me manage new object instances, I have to manage them myself.

What I find really suprising is that AFAIK there should be some kind of test to ensure that all JPA are compatible (something called the TCK?), and I think that this kind of discrepancy should be detected by those tests... shouldn't it?

I think this support for deferred validation, and an API to get will be inserted, will be updated or will be deleted, will be very important to really integrate JPA with something like JSR 330, so that validation can really integrate with the lifecycle of a persistent POJO.

What do you think?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 23, 2009 12:13 am 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
Created a JIRA issue EJB-419 2 days ago... no answer so far...


Top
 Profile  
 
 Post subject: Cheating on persist validation
PostPosted: Mon Mar 23, 2009 12:33 am 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
Another reason for not checking stuff like nullabablity on entityManager.persist, is that it is really so easy (and common) for values to change between the call to persist and the actual commit of the transaction:

Code:
                em.getTransaction().begin();
      Customer customer = new Customer();
      customer.setName("Something");
      em.persist(customer);
      customer.setName(null);      
      em.getTransaction().commit();




See? I set the name of the customer to "Something" and the set it back to null, just to be able to tell the entityManager that I want this customer object to be managed by the persistence engine, but after that, I can set it back to null, and Hibernate will check it again at em.getTransaction().commit();

Code:

JpaTest (1)
jpa.testing.JpaTest
cheatingPersistValidation(jpa.testing.JpaTest)
javax.persistence.RollbackException: Error while commiting the transaction
   at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:71)
   at jpa.testing.JpaTest.cheatingPersistValidation(JpaTest.java:39)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:597)
   at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99)
   at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81)
   at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
   at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75)
   at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45)
   at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:66)
   at org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:35)
   at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42)
   at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
   at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52)
   at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
   at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: org.hibernate.PropertyValueException: not-null property references a null or transient value: jpa.entities.Customer.name
   at org.hibernate.engine.Nullability.checkNullability(Nullability.java:95)
   at org.hibernate.event.def.DefaultFlushEntityEventListener.scheduleUpdate(DefaultFlushEntityEventListener.java:292)
   at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:151)
   at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:219)
   at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99)
   at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:49)
   at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
   at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:365)
   at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
   at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:54)
   ... 21 more




Why waste time and reduce coding flexibility by checking nullability twice? I should only do it on em.getTransaction().commit(); as other JPA implementations do (or at least let me control if I really want to redundantly check it on entityManager.persist)


Top
 Profile  
 
 Post subject: Re: EntityManager.persist: ¿What does/should it mean?
PostPosted: Wed May 27, 2009 6:18 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Hibernate' behavior is correct. nullable=true is defined by the spec as a hint for schema generation.

http://java.sun.com/javaee/5/docs/api/j ... #nullable()

For object-level validation, use optional=false:

http://java.sun.com/javaee/5/docs/api/j ... #optional()


Top
 Profile  
 
 Post subject: Re: EntityManager.persist: ¿What does/should it mean?
PostPosted: Tue Jun 02, 2009 10:33 am 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
gavin wrote:
Hibernate' behavior is correct. nullable=true is defined by the spec as a hint for schema generation.

http://java.sun.com/javaee/5/docs/api/j ... #nullable()



That should be: 'EclipseLink' and 'Hibernate' behavior is incorrect. nullable=true is defined by the spec as a hint for schema generation.

Hibernate uses it to validate immediately after entityManager.persist is called (and as (not only) a hint for schema generation)

EclipseLink uses it to validate transactionally after (and as (not only) a hint for schema generation)

Or maybe we should say that the JPA spec is imprecise and does not say that nullable=true is to be used only as a hint for schema generation and not for any kind of validation (not on persist, not on transaction commit?)

Quote:

For object-level validation, use optional=false:

http://java.sun.com/javaee/5/docs/api/j ... #optional()



So what you are saying is if I use:

Code:

@Column(nullable=false, optional=true)
public String getName() {
return name;
}



I should get a database error instead of an Hibernate error? Let's test that...


Top
 Profile  
 
 Post subject: Re: EntityManager.persist: ¿What does/should it mean?
PostPosted: Tue Jun 02, 2009 10:40 am 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
Made a mistake when writing, what needs to be tested is this:

Code:
@Column(nullable=false)
@Basic(optional=true)
public String getName() {
return name;
}


Since @Column(nullable=false) is just a hint for schema generation, the error should come from the database, the moment the INSERT is sent to the database.


Top
 Profile  
 
 Post subject: Re: EntityManager.persist: ¿What does/should it mean?
PostPosted: Tue Jun 02, 2009 10:48 am 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
luxspes wrote:
Made a mistake when writing, what needs to be tested is this:


Since @Column(nullable=false) is just a hint for schema generation, the error should come from the database, the moment the INSERT is sent to the database.



Well, Hibernate is still wrong, Hibernate does not use @Column(nullable=false) is just a hint for schema generation:

Code:
avax.persistence.PersistenceException: org.hibernate.PropertyValueException: not-null property references a null or transient value: jpa.entities.Customer.name
   at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:614)
   at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:226)
   at jpa.testing.JpaPersistTest.persistCustomerInTransaction(JpaPersistTest.java:61)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:597)
   at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99)
   at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81)
   at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
   at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75)
   at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45)
   at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:66)
   at org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:35)
   at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42)
   at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
   at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52)
   at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
   at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: org.hibernate.PropertyValueException: not-null property references a null or transient value: jpa.entities.Customer.name
   at org.hibernate.engine.Nullability.checkNullability(Nullability.java:95)
   at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:313)
   at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
   at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130)
   at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49)
   at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:154)
   at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:110)
   at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61)
   at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:645)
   at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:619)
   at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:623)
   at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:220)
   ... 21 more


The problem is still at at org.hibernate.engine.Nullability.checkNullability(Nullability.java:95) where, by what you say, the spec is ignored and @Column(nullable=false) is used for more than "just a hint for schema generation"


Top
 Profile  
 
 Post subject: Re: EntityManager.persist: ¿What does/should it mean?
PostPosted: Tue Jun 02, 2009 10:55 am 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
Oh, and this gets even better, if I do the same in Eclipse Link I get: an SQLException! Is I should! As the spec says!
So, I guess only Hibernate is wrong:

Code:
javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 1.1.0.r3634): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Attempt to insert null into a non-nullable column: column: NAME table: CUSTOMER in statement [INSERT INTO CUSTOMER (ID, NAME) VALUES (?, ?)]
Error Code: -10
Call: INSERT INTO CUSTOMER (ID, NAME) VALUES (?, ?)
   bind => [1, null]
Query: InsertObjectQuery(jpa.entities.Customer@16d2633)
   at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:102)
   at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:63)
   at jpa.testing.JpaPersistTest.persistCustomerInTransaction(JpaPersistTest.java:37)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:597)
   at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99)
   at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81)
   at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
   at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75)
   at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45)
   at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:66)
   at org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:35)
   at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42)
   at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
   at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52)
   at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
   at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 1.1.0.r3634): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Attempt to insert null into a non-nullable column: column: NAME table: CUSTOMER in statement [INSERT INTO CUSTOMER (ID, NAME) VALUES (?, ?)]
Error Code: -10
Call: INSERT INTO CUSTOMER (ID, NAME) VALUES (?, ?)
   bind => [1, null]
Query: InsertObjectQuery(jpa.entities.Customer@16d2633)
   at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:313)
   at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:767)
   at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:833)
   at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:558)
   at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:501)
   at org.eclipse.persistence.internal.sessions.AbstractSession.executeCall(AbstractSession.java:872)
   at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:205)
   at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:191)
   at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.insertObject(DatasourceCallQueryMechanism.java:330)
   at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:162)
   at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:177)
   at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:461)
   at org.eclipse.persistence.queries.InsertObjectQuery.executeCommit(InsertObjectQuery.java:80)
   at org.eclipse.persistence.queries.InsertObjectQuery.executeCommitWithChangeSet(InsertObjectQuery.java:90)
   at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:286)
   at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
   at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:664)
   at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:583)
   at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:109)
   at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:86)
   at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2697)
   at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1181)
   at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1165)
   at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1125)
   at org.eclipse.persistence.internal.sessions.CommitManager.commitNewObjectsForClassWithChangeSet(CommitManager.java:195)
   at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:103)
   at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:3175)
   at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1277)
   at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitToDatabase(RepeatableWriteUnitOfWork.java:468)
   at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1373)
   at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:160)
   at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1038)
   at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:84)
   ... 22 more
Caused by: java.sql.SQLException: Attempt to insert null into a non-nullable column: column: NAME table: CUSTOMER in statement [INSERT INTO CUSTOMER (ID, NAME) VALUES (?, ?)]
   at org.hsqldb.jdbc.Util.throwError(Unknown Source)
   at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source)
   at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:758)
   ... 53 more


Top
 Profile  
 
 Post subject: Re: EntityManager.persist: ¿What does/should it mean?
PostPosted: Tue Jun 02, 2009 11:18 am 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
And the even worse thing is that if I change

Code:
@Column(nullable=false)
@Basic(optional=true)
public String getName() {
return name;
}


to this

Code:
@Column(nullable=false)
@Basic(optional=false)
public String getName() {
return name;
}


The behavior is exactly the same, both EclipseLink and Hibernate couldn't care less about @Basic(optional=true/false). Guess not even those who write the RI read the specs ;)


Top
 Profile  
 
 Post subject: Re: EntityManager.persist: ¿What does/should it mean?
PostPosted: Tue Jun 02, 2009 12:16 pm 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
gavin wrote:
Hibernate' behavior is correct. nullable=true is defined by the spec as a hint for schema generation.

http://java.sun.com/javaee/5/docs/api/j ... #nullable()

For object-level validation, use optional=false:

http://java.sun.com/javaee/5/docs/api/j ... #optional()


Mmmm, I decided to accept your intepretation of the spec and created a new JIRA issue (EJB-441) . But now I am re-reading the page 179 of ejb-3_0-fr-spec-persistence.pdf (the document with JSR 220: Enterprise JavaBeansTM,Version 3.0) in table 12, it about @Basic and optional=false:

boolean optional (Optional) Whether the value of the field or property may be null. This is a hint and is disregarded for primitive types; it may be used in schema generation.

And on nullable of @Column:

boolean nullable (Optional) Whether the database column is nullable.

Schema generation is not mentioned at all.

So much for @Basic being only for object-level validation, and @Column only for schema generation... and no mention on what to do if you have a case like the one I exposed previously:

Code:
@Column(nullable=false)
@Basic(optional=true)
public String getName() {
return name;
}


If we follow Gavin's interpretation of the spec that "maybe (or may be? ;-) )" contradicts the spec (someone should forbid both "maybe" and "may be" from specs, it either does, or does not), then that should create a not nullable field in the database that is not validated at the object-level, and that should crash with a java.sql.SQLException, and that is what EclipseLink does (and Hibernate does not)

What I am confused about is this: Did I detected an inconsistency in Hibernate behavior, in the JPA spec, or in both? ;-). So far, based on the fact that EclipseLink seems to interpret the spec the way Gavin interprets it, I am willing to accept that as the right interpretation, and in that case, EclipseLink does it right, and Hibernate has a bug...

I hope I do not have to wait a month for an answer, like last time... :'(


Top
 Profile  
 
 Post subject: Re: EntityManager.persist: ¿What does/should it mean?
PostPosted: Mon Aug 03, 2009 12:11 am 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
Well, since I have got no answer in 2 months.I have asked the guys at the JSR-317 expert group to please clarify this issue.


Top
 Profile  
 
 Post subject: Re: EntityManager.persist: ¿What does/should it mean?
PostPosted: Tue Sep 01, 2009 12:18 am 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
Make that 3 months without an answer (and it seems that the expert group also does not care about this... after all making JPA implementations actually compatible means no vendor lock up, and nobody in their right mind could want that...)


Top
 Profile  
 
 Post subject: Re: EntityManager.persist: ¿What does/should it mean?
PostPosted: Mon Oct 05, 2009 2:36 am 
Newbie

Joined: Mon Oct 05, 2009 1:57 am
Posts: 1
Hi.

Got the same problem as you.

We wanna switch our product from a no longer maintained persistence framework to hibernate. At our system it is very important to have an id immediately, but before the user has choosen to fill other non optional relations. And it is a high risk using detached/transient objects at our system.


I understand, that it might be important to validate the values/relations at persist, cause it is possible that hibernate inserts a row into the database to get the id.

But if id generation needs no insert, it should be possible to turn off this validation.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 13 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.