Does it harm anything to include an Entity in a transaction twice? - jpa-2.0

I am currently programming a tricky update task, where individual Entity objects might be updated more than one time in an atomic transaction. My merge operation looks like this (omitting the try / catch for clarity):
#PersistenceContext protected EntityManager em;
#Resource protected UserTransaction utx;
...
utx.begin();
for (Record r : listOfRecords) em.merge(r);
utx.commit();
If there are multiple references to a given Entity in the list, does anything bad happen?
This is with EclipseLink 2.3.2 as the JPA provider.

Persistent Context : A persistence context is a set of managed entity instances in which for any persistent entity identity there is a unique entity instance. Within the persistence context, the entity instances and their lifecycle are managed by the entity manager.
merge() : Merge the state of the given entity into the current persistence context.
Therefore, there will exist only one instance of an entity at a time in the persistent context. So even if you update your entity multiple times, it will be reflected into database only once, when the transaction commits.

Related

Doctrine swap out table at runtime

Typically when you implement a entity using Doctrine you map it to a table explicitly:
<?php
/**
* #Entity
* #Table(name="message")
*/
class Message
{
//...
}
Or you reply on doctrine to implicitly map your class name to a table...I have several tables which are identical in schema but I do not wish to re-create the class for each time...there fore at runtime (dynamically) I would like to change the table name accordingly.
Where do I start or what would I look into overriding to implement this odd requirement???
Surprisingly (to me), the solution is very simple. All you have to do is to get the ClassMetadata of your entity and change the name of the table it maps to:
/** #var EntityManager $em */
$class = $em->getClassMetadata('Message');
$class->setPrimaryTable(['name' => 'message_23']);
You need to be careful and do not change the table name after you have loaded some entities of type Message and changed them. It's a big chance it will either produce SQL errors on saving (because of table constraints, for example), if you are lucky or it will modify the wrong row (from the new table).
I suggest the following workflow:
set the desired table name;
load some entities;
modify them at will;
save them;
detach them from the entity manager (the method EntityManager::clear() is a quick way to start over);
go back to step 1 (i.e. repeat using another table).
The step #5 (detach the entities from the entity manager) is useful even if you don't change or don't save the entities. It allows the entity manager use less memory and work faster.
This is just one of the many methods you can use to dynamically set/change the mapping. Take a look at the documentation of class ClassMetadata for the rest of them. You can find more inspiration in the documentation page of the PHP mapping.

Adding Entity with Many-To-One association and inverse side already existing in the database

Using Doctrine 2 with Zend Framework 2. Been thinking of how I am meant to persist an entity with a field having a Many-To-One association with another entity that already exists in the database. Would I have to fetch the inverse side from the database first and then adding it to the owning Entity before persisting to the database as in the code below.
//$data = $_POST;
$book = new Book();
$author = $em->getRepository('Application\Entity\Book')->find($data['author_id']);
$book->setTitle($data['title'])
->setISBN($data['title'])
->setAbstract($data['abstract'])
->setYear($data['year'])
->setAuthor($author);
$em->persist($book);
$em->flush();
Normally, without using doctrine, all I have to do is update the author_id field of the Book entity above and persist to the Database. But now I have to make a trip to the Database to fetch the author details to create the entity and thus the association and then persist.
Is this the way it should be done or there is another way that doesnt involve fetching the author's details.
As you can read here
The method EntityManager#getReference($entityName, $identifier) lets
you obtain a reference to an entity for which the identifier is known,
without loading that entity from the database. This is useful, for
example, as a performance enhancement, when you want to establish an
association to an entity for which you have the identifier.
You could simply do this:
$book = new Book();
$book->setAuthor( $em->getReference('Application\Entity\Author',$data['author_id']));

JPA 2.0 Entity update with new relationship fails on persist but succeeds on merge

I have the following relationships defined
Entity A has #OneToMany with Entity B that has #OneToOne with Entity C.
At the time of creation for Entity A, I also create Entity B and set the relationship between them. Everything works as expected.
In another transaction,
- Entity A is fetched from the DB, the corresponding Entity B is fetched and then
- Entity C is created.
- Entity C is set on Entity B to forge the relationship between the two.
If i use EntityManager.persist(Entity B), the persistence manager tries and inserts Entity B into the DB again causing a unique constraint violation on the PK of Entity B.
If i user EntityManager.merge(Entity B), everything works as desired.
In this above context of use, why would a persist operation try and create a duplicate since the Entity is managed in this case and it should easily be able to do an update.
JPA requires providers to throw an exception if persist is called on an unmanaged instance. Only merge can be used since it is clear that it takes the data from the instance and merges it into a managed copy. If B were managed, the persist call would be ignored, so it means that some how B gets detached or that it was read in a different entityManager from the one you call persist on.

How to handle EntityNotFoundException in Spring Data JPA (or just standard JPA 2)

I am in the process of cleaning a database. These process involves changing the format of certain fields and getting rid of some data integrity issues.
I developed a program with Spring Data 1.1 to process the records in batches. The problem arises with 2 entities in a #OneToOne relationship. The record for Entity B does not exist although Entity A has a reference to it. My job is to clear the reference to Entity B if that is the case.
The question is: should I pre-process the data to clean this or can I adjust Spring Data or JPA settings to put null in the field if the Entity is not found?
It is "normal" - with this data - to have a FK in Entity A that does not exist in Entity B, so I want to handle this in my code and not have to pre-process the data with an additional step or other tool. The data will be arriving in batches so any pre-processing makes things more complicated for the user.
In summary, I want Spring Data to set the field to null and continue the process instead of getting an org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find....
Perhaps you are looking for the #NotFound annotation?
Here is a post that talks about it.
I have had the same problem because my orm mapping has a wrong #One-to-one unidirectional relationship.

Comparing entities while unit testing with Hibernate

I am running JUnit tests using in memory HSQLDB. Let's say I have a method that inserts some values to the DB and I am checking if the method inserted the values correctly. Note that order of the insertion is not important.
#Test
public void should_insert_correctly() {
MyEntity[] expectedEntities = new MyEntity[2];
// init expected entities
Inserter out = new Inserter(session); // out: object under test
out.insert();
List list = session.createCriteria(MyEntity.class).list();
assertTrue(list.contains(expectedEntities[0]));
assertTrue(list.contains(expectedEntities[1]));
}
The problem is I cannot compare expected entities to actual ones because the expected's id and the actual's id are different. Since setId() of MyEntity is private (to prevent setting id explicitly), I cannot set all of the entities' id to 0 and compare like that.
How can I compare two result set regardless of their ids?
I found this more practical. Instead of fetching all results at once, I am fetching results according to the criterias and asserting they are not null.
public void should_insert_correctly() {
Inserter out = new Inserter(session); // out: object under test
out.insert();
Criteria criteria;
criteria = getCriteria(session, 0);
assertNotNull(criteria.uniqueResult());
criteria = getCriteria(session, 1);
assertNotNull(criteria.uniqueResult());
}
private Criteria getCriteria(Session session, int i) {
Criteria criteria = session.createCriteria(MyEntity.class);
criteria.add(Restrictions.eq("x", expectedX[i]));
criteria.add(Restrictions.eq("y", expectedY[i]));
return criteria;
}
A stateful entity should not override equals -- that is, entities should be compared for equality by reference identity -- so List.contains will not work as you want.
What I do is use reflection to compare the fields of the original and reloaded entities. The function that walks over the fields of the objects ignores transient fields and those annotated as #Transient.
I don't find I need to ignore the id. When the object is first flushed to the database, Hibernate allocates it an id. When it is reloaded, the object will have the same id.
The flaw in your test is that you have not set transaction boundaries. You need to save the objects in one transaction. When you commit that transaction, Hibernate will flush the objects to the database and allocate their ids. Then in another transaction load the entities back from the database. You will get another set of objects that should have the same ids and persistent (i.e. non-transient) state.
I would try to implement Object.equals(Object) method in your MyEntity class.
List.contains(Object) uses Object.equals(Object) (Source: Java 6 API) to determine if an Object is in this list.
The method session.createCriteria(MyEntity.class).list(); returns a list of new instances with the values you inserted (hopefully).
So you need to compare the values. This is easily done via the implementation of Object.equals(Object).
Clarification edit:
You could ignore the ids in your equals method, so that the comparison only cares about "real values".
YAE (Yet Another Edit):
I recommend reading this article about the equals() method: Angelika Langer: Secrets Of Equal. It explains all background information very well.