Detach an entity from JPA/EJB3 persistence context

JavaOrmJpa

Java Problem Overview


What would be the easiest way to detach a specific JPA Entity Bean that was acquired through an EntityManager. Alternatively, could I have a query return detached objects in the first place so they would essentially act as 'read only'?

The reason why I want to do this is becuase I want to modify the data within the bean - with in my application only, but not ever have it persisted to the database. In my program, I eventually have to call flush() on the EntityManager, which would persist all changes from attached entities to the underyling database, but I want to exclude specific objects.

Java Solutions


Solution 1 - Java

(may be too late to answer, but can be useful for others)

I'm developing my first system with JPA right now. Unfortunately I'm faced with this problem when this system is almost complete.

Simply put. Use Hibernate, or wait for JPA 2.0.

In Hibernate, you can use 'session.evict(object)' to remove one object from session. In JPA 2.0, in draft right now, there is the 'EntityManager.detach(object)' method to detach one object from persistence context.

Solution 2 - Java

No matter which JPA implementation you use, Just use entityManager.detach(object) it's now in JPA 2.0 and part of JEE6.

Solution 3 - Java

If you need to detach an object from the EntityManager and you are using Hibernate as your underlying ORM layer you can get access to the Hibernate Session object and use the Session.evict(Object) method that Mauricio Kanada mentioned above.

public void detach(Object entity) {
    org.hibernate.Session session = (Session) entityManager.getDelegate();
    session.evict(entity);
}

Of course this would break if you switched to another ORM provider but I think this is preferably to trying to make a deep copy.

Solution 4 - Java

Unfortunately, there's no way to disconnect one object from the entity manager in the current JPA implementation, AFAIR.

EntityManager.clear() will disconnect all the JPA objects, so that might not be an appropriate solution in all the cases, if you have other objects you do plan to keep connected.

So your best bet would be to clone the objects and pass the clones to the code that changes the objects. Since primitive and immutable object fields are taken care of by the default cloning mechanism in a proper way, you won't have to write a lot of plumbing code (apart from deep cloning any aggregated structures you might have).

Solution 5 - Java

As far as I know, the only direct ways to do it are:

  1. Commit the txn - Probably not a reasonable option
  2. Clear the Persistence Context - EntityManager.clear() - This is brutal, but would clear it out
  3. Copy the object - Most of the time your JPA objects are serializable, so this should be easy (if not particularly efficient).

Solution 6 - Java

If using EclipseLink you also have the options,

Use the Query hint, eclipselink.maintain-cache"="false - all returned objects will be detached.

Use the EclipseLink JpaEntityManager copy() API to copy the object to the desired depth.

Solution 7 - Java

If there aren't too many properties in the bean, you might just create a new instance and set all of its properties manually from the persisted bean.

This could be implemented as a copy constructor, for example:

public Thing(Thing oldBean) {
  this.setPropertyOne(oldBean.getPropertyOne());
  // and so on
}

Then:

Thing newBean = new Thing(oldBean);

Solution 8 - Java

this is quick and dirty, but you can also serialize and deserialize the object.

Solution 9 - Java

Since I am using SEAM and JPA 1.0 and my system has a fuctinality that needs to log all fields changes, i have created an value object or data transfer object if same fields of the entity that needs to be logged. The constructor of the new pojo is:

	public DocumentoAntigoDTO(Documento documentoAtual) {
	Method[] metodosDocumento = Documento.class.getMethods();
	for(Method metodo:metodosDocumento){
		if(metodo.getName().contains("get")){
			try {
				Object resultadoInvoke = metodo.invoke(documentoAtual,null);
				Method[] metodosDocumentoAntigo = DocumentoAntigoDTO.class.getMethods();
				for(Method metodoAntigo : metodosDocumentoAntigo){
					String metodSetName = "set" + metodo.getName().substring(3);
					if(metodoAntigo.getName().equals(metodSetName)){
						metodoAntigo.invoke(this, resultadoInvoke);
					}
				}
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
	}
}

Solution 10 - Java

In JPA 1.0 (tested using EclipseLink) you could retrieve the entity outside of a transaction. For example, with container managed transactions you could do:

public MyEntity myMethod(long id) {
	final MyEntity myEntity = retrieve(id);
	// myEntity is detached here
}

@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public MyEntity retrieve(long id) {
	return entityManager.find(MyEntity.class, id);
}

Solution 11 - Java

Do deal with a similar case I have created a DTO object that extends the persistent entity object as follows:

class MyEntity
{
   public static class MyEntityDO extends MyEntity {}

}

Finally, an scalar query will retrieve the desired non managed attributes:

(Hibernate) select p.id, p.name from MyEntity P
(JPA)       select new MyEntity(p.id, p.name) from myEntity P

Solution 12 - Java

If you get here because you actually want to pass an entity across a remote boundary then you just put some code in to fool the hibernazi.

for(RssItem i : result.getChannel().getItem()){
}

Cloneable wont work because it actually copies the PersistantBag across.

And forget about using serializable and bytearray streams and piped streams. creating threads to avoid deadlocks kills the entire concept.

Solution 13 - Java

I think there is a way to evict a single entity from EntityManager by calling this

EntityManagerFactory emf;
emf.getCache().evict(Entity);

This will remove particular entity from cache.

Solution 14 - Java

Im using entityManager.detach(returnObject); which worked for me.

Solution 15 - Java

I think you can also use method EntityManager.refresh(Object o) if primary key of the entity has not been changed. This method will restore original state of the entity.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMatthew RustonView Question on Stackoverflow
Solution 1 - JavaMauricio KanadaView Answer on Stackoverflow
Solution 2 - JavaMehdiView Answer on Stackoverflow
Solution 3 - JavaJames McMahonView Answer on Stackoverflow
Solution 4 - JavaAndreiView Answer on Stackoverflow
Solution 5 - JavajsightView Answer on Stackoverflow
Solution 6 - JavaJamesView Answer on Stackoverflow
Solution 7 - JavatonygamboneView Answer on Stackoverflow
Solution 8 - JavaDavide ConsonniView Answer on Stackoverflow
Solution 9 - JavaDavid FaulstichView Answer on Stackoverflow
Solution 10 - JavaChris BView Answer on Stackoverflow
Solution 11 - JavaRoger MoriView Answer on Stackoverflow
Solution 12 - JavaWarren CrossingView Answer on Stackoverflow
Solution 13 - JavaAbhishek TripathiView Answer on Stackoverflow
Solution 14 - JavaPradeeshView Answer on Stackoverflow
Solution 15 - JavatrunikovView Answer on Stackoverflow