Using EclipseLink 2.6.0 on WebLogic 12.2.1.4.0 I started to get the following exception upon invoking EntityManager.merge()
java.lang.NullPointerException: null
at org.eclipse.persistence.descriptors.changetracking.AttributeChangeTrackingPolicy.updateListenerForSelfMerge(AttributeChangeTrackingPolicy.java:130)
at org.eclipse.persistence.mappings.CollectionMapping.mergeIntoObject(CollectionMapping.java:1642)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.mergeIntoObject(ObjectBuilder.java:4136)
at org.eclipse.persistence.internal.sessions.MergeManager.mergeChangesOfCloneIntoWorkingCopy(MergeManager.java:601)
at org.eclipse.persistence.internal.sessions.MergeManager.mergeChanges(MergeManager.java:313)
at org.eclipse.persistence.mappings.CollectionMapping.mergeIntoObject(CollectionMapping.java:1638)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.mergeIntoObject(ObjectBuilder.java:4136)
at org.eclipse.persistence.internal.sessions.MergeManager.mergeChangesOfCloneIntoWorkingCopy(MergeManager.java:601)
at org.eclipse.persistence.internal.sessions.MergeManager.mergeChanges(MergeManager.java:313)
at org.eclipse.persistence.mappings.CollectionMapping.mergeIntoObject(CollectionMapping.java:1638)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.mergeIntoObject(ObjectBuilder.java:4136)
at org.eclipse.persistence.internal.sessions.MergeManager.mergeChangesOfCloneIntoWorkingCopy(MergeManager.java:601)
at org.eclipse.persistence.internal.sessions.MergeManager.mergeChanges(MergeManager.java:313)
at org.eclipse.persistence.mappings.ObjectReferenceMapping.mergeIntoObject(ObjectReferenceMapping.java:499)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.mergeIntoObject(ObjectBuilder.java:4136)
at org.eclipse.persistence.internal.sessions.MergeManager.mergeChangesOfCloneIntoWorkingCopy(MergeManager.java:601)
at org.eclipse.persistence.internal.sessions.MergeManager.mergeChanges(MergeManager.java:313)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.mergeCloneWithReferences(UnitOfWorkImpl.java:3524)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.mergeCloneWithReferences(RepeatableWriteUnitOfWork.java:387)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.mergeCloneWithReferences(UnitOfWorkImpl.java:3484)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.mergeInternal(EntityManagerImpl.java:553)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.merge(EntityManagerImpl.java:530)
at weblogic.persistence.BasePersistenceContextProxyImpl.invoke(BasePersistenceContextProxyImpl.java:97)
at weblogic.persistence.TransactionalEntityManagerProxyImpl.invoke(TransactionalEntityManagerProxyImpl.java:164)
at weblogic.persistence.BasePersistenceContextProxyImpl.invoke(BasePersistenceContextProxyImpl.java:86)
at com.sun.proxy.$Proxy5085.merge(Unknown Source)
at com.acme.dao.Dao.save(Dao.java:86)
I found a workaround which is setting
<property name="eclipselink.weaving.changetracking" value="false"/>
in persistence.xml
However, this disables weaving changetracking for the whole persistence unit which might have an undesired global impact.
The transaction involves a bunch of objects with twisted #ManyToOne(fetch = FetchType.LAZY) relationships and the exception is thrown only when trying to delete one of the objects and merging another.
My question is: is it possible to disable weaving changetracking only for the entities involved in the transaction where the NullPointerException occurs?
I have tried adding #Mutable annotations on the fields and all types of #ChangeTracking on the entities but none of those eliminated the NullPointerException.
I have just created and joined 3 new tables. Before creating them everything was working fine so I do believe the root cause is coming from their creation. Now when runned in the browser I receive this error:
org.apache.jasper.JasperException: Exception [EclipseLink-7242]
(Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd):
org.eclipse.persistence.exceptions.ValidationException
Exception Description: An attempt was made to traverse a relationship using indirection that had a null Session. This often
occurs when an entity with an uninstantiated LAZY relationship is
serialized and that lazy relationship is traversed after
serialization. To avoid this issue, instantiate the LAZY relationship
prior to serialization.
How does one instantiate the LAZY relationship prior to serialization anyway? I've been looking all over the web but I still haven't found real working solutions...
I also receive this other error message:
Exception [EclipseLink-4002] (Eclipse Persistence Services -
2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:
Unknown column 't1.rating' in 'field list' Error Code: 1054 Call:
SELECT t1.id, t1.description, t1.last_update, t1.name, t1.price,
t1.rating FROM category_has_product t0, product t1 WHERE
((t0.category_id = ?) AND (t1.id = t0.product_id)) bind => [1
parameter bound] Query: ReadAllQuery(name="productCollection"
referenceClass=Product sql="SELECT t1.id, t1.description,
t1.last_update, t1.name, t1.price, t1.rating FROM category_has_product
t0, product t1 WHERE ((t0.category_id = ?) AND (t1.id =
t0.product_id))")
root cause
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown
column 't1.rating' in 'field list'
What do all those errors mean? I don't understand because all the columns and the rows are perfectly well mapped... It says it canno't find certain columns... I have checked all my tables over and over and I really don't understand what could possibly be wrong... Everything seems fine... A little help would be very much appreciated! Thanks
Besides I use the following for the app:
Netbeans
Glassfish
Mysql
Most pages in jsp
Problem with instantiate lazy reference is well known, so I will provide a link rather than explain myself: http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Serialization.2C_and_Detaching
Wiki page has also 3 solutions to solve the problem. But I will offer you another solution, which may suits your case: JSP usage.
Check https://dzone.com/articles/open-session-view-design (or just google for 'open session in view'. Many pages refers to Hibernate but it applies to Eclipselink also). The solution is start and end transaction in servlet filter. The transaction will cover whole request processing. In your case JSP page generation will be within transaction and lazy references will just work. Benefit is that you don't have to modify your code.
I have no idea about your second problem with mapping
usually I would set
<property name="hibernate.hbm2ddl.auto" value="validate"/>
in the persistence.xml if I would like to check if my database is consistent with my annotated entities (Am I right?)
Now this validation would fail at application start up because there will be some migrations after, so I have to disable validation at this place.
But after my migrations I would like to run anything out of my code which compares that my database tables meet the annotated entities of my code!
The Validation looks for perfection you dont need. In example, the Range of java.sql.Date may differs from the range of the congrete Column type Date.
Anyway, try this after your startup and migrations:
AnnotationConfiguration conf = new AnnotationConfiguration();
conf.addClass(...);
conf.validateSchema(...);
When I try to delete an entry from a db, using
session.delete(object)
then I can the following:
1) If the row is present in DB then two SQL queries are getting executed: A select and then a delete
2) If the row is not present in the DB then only the select query is getting executed
But again this is not the case for update. Irrespective of the presence of DB row, only the update query is getting executed.
Please let me know why this kind of behaviour for delete operation. Isn't it a performance issue since two queries are getting hit rather than one?
Edit:
I am using hibernate 3.2.5
Sample code:
SessionFactory sessionFactory = new Configuration().configure("student.cfg.xml").buildSessionFactory();
Session session = sessionFactory.openSession();
Student student = new Student();
student.setFirstName("AAA");
student.setLastName("BBB");
student.setCity("CCC");
student.setState("DDD");
student.setCountry("EEE");
student.setId("FFF");
session.delete(student);
session.flush();
session.close();
cfg.xml
<property name="hibernate.connection.username">system</property>
<property name="hibernate.connection.password">XXX</property>
<property name="hibernate.connection.driver_class">oracle.jdbc.OracleDriver</property>
<property name="hibernate.connection.url">jdbc:oracle:thin:#localhost:1521/orcl</property>
<property name="hibernate.jdbc.batch_size">30</property>
<property name="hibernate.dialect">org.hibernate.dialect.OracleDialect</property>
<property name="hibernate.cache.use_query_cache">false</property>
<property name="hibernate.cache.use_second_level_cache">false</property>
<property name="hibernate.connection.release_mode">after_transaction</property>
<property name="hibernate.connection.autocommit">true</property>
<property name="hibernate.connection.pool_size">0</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.infy.model.Student" table="STUDENT">
<id name="id" column="ID">
<generator class="assigned"></generator>
</id>
<property name="firstName" type="string" column="FIRSTNAME"></property>
<property name="lastName" type="string" column="LASTNAME"></property>
<property name="city" type="string" column="CITY"></property>
<property name="state" type="string" column="STATE"></property>
<property name="country" type="string" column="COUNTRY"></property>
</class>
The reason is that for deleting an object, Hibernate requires that the object is in persistent state. Thus, Hibernate first fetches the object (SELECT) and then removes it (DELETE).
Why Hibernate needs to fetch the object first? The reason is that Hibernate interceptors might be enabled (http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/events.html), and the object must be passed through these interceptors to complete its lifecycle. If rows are delete directly in the database, the interceptor won't run.
On the other hand, it's possible to delete entities in one single SQL DELETE statement using bulk operations:
Query q = session.createQuery("delete Entity where id = X");
q.executeUpdate();
To understand this peculiar behavior of hibernate, it is important to understand a few hibernate concepts -
Hibernate Object States
Transient - An object is in transient status if it has been
instantiated and is still not associated with a Hibernate session.
Persistent - A persistent instance has a representation in the
database and an identifier value. It might just have been saved or
loaded, however, it is by definition in the scope of a Session.
Detached - A detached instance is an object that has been persistent,
but its Session has been closed.
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/objectstate.html#objectstate-overview
Transaction Write-Behind
The next thing to understand is 'Transaction Write behind'. When objects attached to a hibernate session are modified they are not immediately propagated to the database. Hibernate does this for at least two different reasons.
To perform batch inserts and updates.
To propagate only the last change. If an object is updated more than once, it still fires only one update statement.
http://learningviacode.blogspot.com/2012/02/write-behind-technique-in-hibernate.html
First Level Cache
Hibernate has something called 'First Level Cache'. Whenever you pass an object to save(), update() or saveOrUpdate(), and whenever you retrieve an object using load(), get(), list(), iterate() or scroll(), that object is added to the internal cache of the Session. This is where it tracks changes to various objects.
Hibernate Intercepters and Object Lifecycle Listeners -
The Interceptor interface and listener callbacks from the session to the application, allow the application to inspect and/or manipulate properties of a persistent object before it is saved, updated, deleted or loaded.
http://docs.jboss.org/hibernate/orm/4.0/hem/en-US/html/listeners.html#d0e3069
This section Updated
Cascading
Hibernate allows applications to define cascade relationships between associations. For example, 'cascade-delete' from parent to child association will result in deletion of all children when a parent is deleted.
So, why are these important.
To be able to do transaction write-behind, to be able to track multiple changes to objects (object graphs) and to be able to execute lifecycle callbacks hibernate needs to know whether the object is transient/detached and it needs to have the object in it's first level cache before it makes any changes to the underlying object and associated relationships.
That's why hibernate (sometimes) issues a 'SELECT' statement to load the object (if it's not already loaded) in to it's first level cache before it makes changes to it.
Why does hibernate issue the 'SELECT' statement only sometimes?
Hibernate issues a 'SELECT' statement to determine what state the object is in. If the select statement returns an object, the object is in detached state and if it does not return an object, the object is in transient state.
Coming to your scenario -
Delete - The 'Delete' issued a SELECT statement because hibernate needs to know if the object exists in the database or not. If the object exists in the database, hibernate considers it as detached and then re-attches it to the session and processes delete lifecycle.
Update - Since you are explicitly calling 'Update' instead of 'SaveOrUpdate', hibernate blindly assumes that the object is in detached state, re-attaches the given object to the session first level cache and processes the update lifecycle. If it turns out that the object does not exist in the database contrary to hibernate's assumption, an exception is thrown when session flushes.
SaveOrUpdate - If you call 'SaveOrUpdate', hibernate has to determine the state of the object, so it uses a SELECT statement to determine if the object is in Transient/Detached state. If the object is in transient state, it processes the 'insert' lifecycle and if the object is in detached state, it processes the 'Update' lifecycle.
I'm not sure but:
If you call the delete method with a non transient object, this means first fetched the object from the DB. So it is normal to see a select statement. Perhaps in the end you see 2 select + 1 delete?
If you call the delete method with a transient object, then it is possible that you have a cascade="delete" or something similar which requires to retrieve first the object so that "nested actions" can be performed if it is required.
Edit:
Calling delete() with a transient instance means doing something like that:
MyEntity entity = new MyEntity();
entity.setId(1234);
session.delete(entity);
This will delete the row with id 1234, even if the object is a simple pojo not retrieved by Hibernate, not present in its session cache, not managed at all by Hibernate.
If you have an entity association Hibernate probably have to fetch the full entity so that it knows if the delete should be cascaded to associated entities.
instead of using
session.delete(object)
use
getHibernateTemplate().delete(object)
In both place for select query and also for delete use getHibernateTemplate()
In select query you have to use DetachedCriteria or Criteria
Example for select query
List<foo> fooList = new ArrayList<foo>();
DetachedCriteria queryCriteria = DetachedCriteria.forClass(foo.class);
queryCriteria.add(Restrictions.eq("Column_name",restriction));
fooList = getHibernateTemplate().findByCriteria(queryCriteria);
In hibernate avoid use of session,here I am not sure but problem occurs just because of session use
I have recently changed an object to have a #OneToMany mapping to another object, with the FetchType.LAZY. But when I try to load a list of these objects using a #NamedNativeQuery, which calls an Oracle function, it throws a java.sql.SQLException: Invalid column name for this new OneToMany mapping. But being marked as LAZY, it shouldn't try to populate this variable should it?
In theory I could change the function to return an empty value for this column (basically a hack), but I would have to roll that out to everywhere that uses a #NamedNativeQuery to populate one of these objects.
This seems like a bug to me. Is there a workaround, something I'm missing or possibly fixed in a later version of Hibernate?
I'm using hibernate-core 3.3.2.GA, hibernate-entitymanager 3.4.0.GA, hibernate-annotations 3.4.0.GA and hibernate-commons-annotations 3.3.0.ga.