A JPA transaction commit is supposed to save all changes to the entities associated with the persistence context that have happened so far. So it matters a lot where the commit() is located. How much does it matter where the begin() is? Consider the following two scenarios, both of which create entity x, associate x with a persistence context, and later modify that entity and [purport to] save the changes to x to the database:
// Scenario A
EntityManager em = ...
MyEntity x = new MyEntity();
em.getTransaction().begin();
em.persist(x);
em.getTransaction().commit();
// ... do some stuff ...
x.a = val1;
x.setB(val2);
em.getTransaction().begin(); // *a "late begin"*
em.getTransaction().commit();
vs.
// Scenario B
EntityManager em = ...
MyEntity x = new MyEntity();
em.getTransaction().begin();
em.persist(x);
em.getTransaction().commit();
// ... do some stuff ...
em.getTransaction().begin(); // *an "early begin"*
x.a = val1;
x.setB(val2);
em.getTransaction().commit();
In both scenarios, object x belongs to the persistence context of EntityManager em.
Isn't it the case that, in both scenarios, the second commit will save all changes to em's context, including those to x.a and x.b, that have happened since the first commit?
In this case, what is the functional difference between the two scenarios?
Let's assume we use no Spring, Java Beans, and other advanced technologies; just the Java Persistence API and the OpenJPA implementation.
Related
I'm reading Pro JPA 2. It has the following figure:
Per the figure, many entity managers can point to the same persistence context. I'm trying to accomplish this in Java SE:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("EmployeeService");
EntityManager em = emf.createEntityManager();
EntityManager em2 = emf.createEntityManager();
em.getTransaction().begin();
Employee employee = new Employee(1);
employee.setName("Bob");
employee.setSalary(100000);
em.persist(employee);
System.out.println("em" + em.find(Employee.class, 1));
System.out.println("em all Employees " + em.createQuery("SELECT e from Employee e", Employee.class).getResultList());
System.out.println("em2 Employee" + em2.find(Employee.class, 1));
System.out.println("em2 all Employees " + em2.createQuery("SELECT e from Employee e", Employee.class).getResultList());
em.getTransaction().commit();
em.close();
em2.close();
emf.close();
The output is:
em Employee(id=1, name=Bob, salary=100000.0)
em all Employees[Employee(id=1, name=Bob, salary=100000.0)]
em2 Employee null
em2 all Employees[]
So, it appears that em and em2 are pointing to different persistence contexts. Is there a way to have different entity managers manage the same persistence context in Java SE?
I have the following code which I call from the front end
public Login update(Login i) {
em = emf.createEntityManager();
em.getTransaction().begin();
Login result=infoDAO.update(i);
em.getTransaction().commit();
em.close();
return result;
}
public Login update(Login i) {
return em.merge(i);
}
I have
private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("HRTool-JPA");
protected EntityManager em=emf.createEntityManager();
The methods are being called and the values are passed to the DB correctly(I am using Apache derby) but I can see the new changed values in the DB only after I disconnect and reconnect to it. Am I missing some step after merge ? I am new to JPA and appreciate any suggestions on the same
By default Hibernate keep the requests in its cache and Hibernate decides when it wants to execute them.
You can try to add a em.flush() after em.merge()
It will execute all the requests left in its cache.
I have a very basic relationship between two objects:
#Entity
public class A {
#ManyToOne(optional = false)
#JoinColumn(name="B_ID", insertable=false, updatable=true)
private StatusOfA sa;
getter+setter
}
#Entity
public class StatusOfA {
#Id
private long id;
#Column
private String status;
getter+setter
}
There's only a limited set of StatusOfA in DB.
I perform an update on A in a transaction:
#TransactionalAttribute
public void updateStatusOfA(long id) {
A a = aDao.getAById(123);
if(a != null) {
a.getStatusOfA().getId(); //just to ensure that the object is loaded from DB
StatusOfA anotherStatusOfA = statusOfADao.getStatusOfAById(456);
a.setStatusOfA(aontherStatusOfA);
aDao.saveOrPersistA(a);
}
}
The saveOrPersistA method is here merging 'a'.
I expect Eclipselink to perform only an update on 'a' to update the StatusOfA but it's executing a new insert on StatusOfA table. Oracle is then complaining due to a unique contraint violation (the StatusOfA that Eclipselink tries to persist already exists...).
There is no Cascading here so the problem is not there and Hibernate (in JPA2) is behaving as excepted.
In the same project, I already made some more complex relationships and I'm really surprised to see that the relation here in not working.
Thanks in advance for your help.
What does, statusOfADao.getStatusOfAById() do?
Does it use the same persistence context (same transaction and EntityManager)?
You need to use the same EntityManager, as you should not mix objects from different persistence contexts.
What does saveOrPersistA do exactly? The merge() call should resolve everything correctly, but if you have really messed up objects, it may be difficult to merge everything as you expect.
Are you merging just A, or its status as well? Try also setting the status to the merged result of the status.
Assumptions: #Id#GeneratedValue(strategy = GenerationType.IDENTITY)
Let's consider the following implementations of statusOfADao.getStatusOfAById(456) :
1. returns "proxy" object with just id set:
return new StatusOfA(456);
2. returns entity in new transaction:
EntityManager em = emf.createEntityManager();em.getTransaction().begin();
StatusOfA o = em.find(StatusOfA.class,456);//em.getReference(StatusOfA.class,456);
em.getTransaction().commit();
return o;
3. returns detached entity:
StatusOfA o = em.find(StatusOfA.class,456);//em.getReference(StatusOfA.class,456);
em.detached(o);
return o;
4. returns deserialized-serialized entity:
return ObjectCloner.deepCopy(em.find(StatusOfA.class,456));
5. returns attached entity:
return em.find(StatusOfA.class,456);
Conclusions:
Eclipselink handles only implementation N5 as "expected".
Hibernate handles all five implementations as "expected".
No analisys of what behaviour is jpa spec compliant
I'm setting up a basic test data util and want to keep track of all the data that the EntityManager handles. Rather than just having a bunch of lists for each entity is there a way to grab everything being managed by the EntityManager in one fell swoop?
So instead of this:
EntityManager em;
List<Entity1> a;
List<Entity2> b;
...
List<Entityn> n;
cleanup() {
for(Entity1 e : a) em.remove(e);
for(Entity2 f : b) em.remove(f);
...
for(Entityn z : n) em.remove(z);
}
I want something like this;
EntityManager em;
cleanup() {
List<Object> allEntities = em.getAllManagedEntities(); //<-this doesnt exist
for(Object o : allEntities) em.remove(o);
}
Not sure if this is possible, but I just would image that the manager knows what it is managing? Or, if you have any ideas of managing a bunch of entities easily.
I think this might help:
for (EntityType<?> entity : entityManager.getMetamodel().getEntities()) {
final String className = entity.getName();
log.debug("Trying select * from: " + className);
Query q = entityManager.createQuery("from " + className + " c");
q.getResultList().iterator();
log.debug("ok: " + className);
}
Basically EntityManager::MetaModel contains the MetaData information regarding the Entities managed.
What JPA provider are you using?
There is nothing in the JPA API for this.
If using EclipseLink, you can use,
em.unwrap(UnitOfWorkImpl.class).getCloneMapping().keySet()
If you need to remove all entities inserted during a test, you can execute the test inside a transaction and then rollback that transaction. See 9.3.5.4 Transaction management
as an example of this approach.
My current project uses HSQLDB2.0 and JPA2.0 .
The scenario is: I query DB to get list of contactDetails of person. I delete single contactInfo at UI but do not save that data (Cancel the saving part).
I again do the same query, now the result list is 1 lesser than previous result coz I have deleted one contactInfo at UI. But that contactInfo is still available at DB if I cross check.
But if I include entityManager.clear() before start of the query, I get correct results every time.
I dont understand this behaviour. Could anyone make it clear for me?
Rather than querying again, try this:
entityManager.refresh(person);
A more complete example:
EntityManagerFactory factory = Persistence.createEntityManagerFactory("...");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Person p = (Person) em.find(Person.class, 1);
assertEquals(10, p.getContactDetails().size()); // let's pretend p has 10 contact details
p.getContactDetails().remove(0);
assertEquals(9, p.getContactDetails().size());
Person p2 = (Person) em.find(Person.class, 1);
assertTrue(p == p2); // We're in the same persistence context so p == p2
assertEquals(9, p.getContactDetails().size());
// In order to reload the actual patients from the database, refresh the entity
em.refresh(p);
assertTrue(p == p2);
assertEquals(10, p.getContactDetails().size());
assertEquals(10, p2.getContactDetails().size());
em.getTransaction().commit();
em.close();
factory.close();
The behaviour of clear() is explained in its javadoc:
Clear the persistence context, causing all managed entities to become detached. Changes made to entities that have not been flushed to the database will not be persisted.
That is, removal of contactInfo is not persisted.
ContactInfo is not getting removed from the database because you remove the relationship between ContactDetails and ContactInfo, but not ContactInfo itself. If you want to remove it, you need either do it explicitly with remove() or specify orphanRemoval = true on the relationship.