How to persist entities using collection in JPA - java

I'm trying to persist entities in OneToMany relationship using JPA. I have two entities, Proyecto (Project) and Participacion (Participation). Proyecto can have multiple Participacion. The problem is when i try to persist a Participacion to an existing Proyecto.
//First, I get the selected project by the user from the database.
Proyecto proyecto = proyectoManager.getProyecto(Integer.parseInt((String) session.getAttribute("idProyecto")));
//Second, I create and set the Participacion parameters, including the project.
Participacion participacion = new Participacion();
participacion.setIdProyecto(proyecto);
participacion.setLogin(usuario);
participacion.setPorcParti(Integer.parseInt(request.getParameter("porc")));
//Finally I persist participacion with his respective project
proyectoManager.addParticipacion(participacion);
//Here, I'm trying to get the project to check if his Participacion collection contains the new participation.
proyecto = proyectoManager.getProyecto(Integer.parseInt((String) session.getAttribute("idProyecto")));
The problem is in the second time I recover the project from database, because his participation collection doesn't contain the new participation. I've tried using edit() method and even flush after persist() method but it doesn't work.
The next time I deploy the Enterprise Application, the project contains his respective participations, including the new one, but I need it after persist the first time, without deploying again.
Can any one help me? Thank you.

How do you tell JPA that you want to cascade persist the collection?
I mainly use annotations on my domain objects, and usually just add something like:
#OneToMany(cascade=CascadeType.ALL)
This (or similar) question has been answered many times on this site.
Edit
Also, from the code sample above, it seems as you are not adding the Participation instance to the Project. I would think that you need something like:
proyecto.addParticipation(participation);
proyectoManager.saveProyecto(proyecto);
to complete the association.

Related

Hibernate throws error (Detached entity) while trying to retrieve data

While trying to update entity I'm first retrieving it from the database, then I'm mapping the TO from frontend on it using Orika Mapper.
Then I'm trying to retrieve some data not related to this entity using 'JpaRepository' and findAllByOrderByCode method. And while this operation I'm getting a strange error saying that: "An unexpected exception occurred: detached entity passed to persist:".
And this error refers not to the basic field from the entity but to the object from the collection from this entity.
Summarize:
I have entity A which have bidirectional mapping One to Many to the entity B:
class A {
List<B> b;
}
then I want to update whole A with an object from frontend which I mapped using Orika Mapper.
And while trying to get some data I have an error.
I found that Orika by default makes a deep copy for collections so entityA = customsClearanceOrderRepository.findById(requestTo.getId());
entityA which has List of entitiesB and which are tracked and included in persistence context is replaced with a deep copy of them so they have another address and it means their aren't any longer tracked by Hibernate.
So I tried to map those collections by myself, to just update the fields and not create a new object and then the problem has gone.
Everything would be fine but when I removed this line List<SthTo> all = someRefersToDb.findAllByOrderByCode(); // error appears here
then the problem also doesn't exist, even that I'm again using orika which makes this deep copy. And I understand that it works fine because of 'saveAndFlush' in fact while updating makes EntityManager.merge(entity) and the problem with another address for entities is not a problem for that (cause it copies not tracked object into persistence context).
entityA = entityARepository.findById(requestTo.getId());
entityAMapper.map(requestTo, entityA);
List<SthTo> all = someRefersToDb.findAllByOrderByCode(); // error appears here
EntityA entityASaved = entityARepository.saveAndFlush(entityA);
So I want to know what's going on here: someRefersToDb.findAllByOrderByCode();
Is there some kind of checking the state of the entityA?
Everything is by default, I mean there is no magical #Transactional(propagation = Propagation.REQUIRES_NEW) or sth like this.
I know why!
Hibernate while running someRefersToDb.findAllByOrderByCode();
in fact, call also session.flush() which is used to synchronize session data with the database. And since Orika changed the addresses of entities their aren't any longer a part of the persistence context and the synchronization fails.

How to create new Row's without JPA consider it an update?

I'm working in a project right now, here is a piece of code:
public boolean getAll() {
TypedQuery<Tag> query = em.createQuery("SELECT c FROM Tag c WHERE (c.tagName !=?1 AND c.tagName !=?2 AND c.tagName !=?3) ", Tag.class);
query.setParameter(1, "Complete");
query.setParameter(2, "GroupA");
query.setParameter(3, "GroupB");
List<Tag> Tag= query.getResultList();
But when I try to do something like this:
Tag.get(2).setTagName = "Hello";
em.persist(Tag.get(2));
It considers it to be an update instead of a create? How can I make JPA understand that it's not database related, to detach the chains with the Database and create new register only changing its name for example?
Thanks a lot for any help!
Best regards!
EDIT:
Using the em.detach just before changing it values and persisting each of the list worked just fine!
Thanks everyone!
You haven't showed us how you are obtaining your list, but there are two key points here:
everything read in from an EntityManager is managed - JPA checks
these managed objects for changes and will synchronize them with the
database when required (either by committing the transaction or
calling flush).
Calling persist on a managed entity is a no-op - the entity is
already managed, and will be synchronized with the database if it
isn't in there yet.
So the first Tag.get(2).setTagName = "Hello"; call is what causes your update, while the persist is a no-op.
What you need do to instead is create a new instance of your tag object and set the field. Create a clone method on your object that copies everything but the ID field, and then call persist on the result to get an insert for a new Entity.
The decision whether to update or create a new entity object is done based on the primary key. You're probably using an ID on every object. Change or remove it and persist then. This should create a new entry.
If that doesn't work, you might need to detach the object from the Entity Manager first:
em.detach(tagObj);
and persist it afterwards:
em.persist(tagObj);
You can also force an update instead of creation by using
em.merge(tagObj)
There is no equivalent for forced creation AFAIK. persist will do both depending on PK.

JPA [Eclipselink] - How to refresh metadata of Dynamic entities created?

I got to know about possibility of Dynamic entity creation in eclipselink from here. And I'm trying to create Dynamic entities and map them to static entities which are already present in the same persistence unit as described in the examples given here.
I'm using refreshMetadata(with empty map of properties) of EntityManagerFactoryImpl to refresh metadata.
But the the dynamic entities are not getting listed in the metamodel of entitymanager factory.
Can somebody let me know where am I going wrong?
I expect they won't, as the Dynamic entity api adds mappings to the native EclipseLink session, while the JPA metamodel is build from JPA mappings. refreshMetadata is used to rebuild the native EclipseLink session using any new JPA metadata (orm.xml etc), but does not go the other way.
I was able to refresh the metamodel by adding a new metamodel with the current session by the following code snippet:
Metamodel metamodel = new MetamodelImpl((AbstractSession) dynamicHelper.getSession());
((EntityManagerFactoryImpl) emf).setMetamodel(metamodel);
Though this didn't solved my main problem, it solved the problem I've asked here.

Edit JPA-Mapping in IntelliJ Idea

I'm having a JPA-Project in IntelliJ Idea and there are some entities my colleague mapped some time ago. Now the DB team added a bunch of tables I'm trying to add as entities to the Java-Project. But when I'm trying to map a new entity to a existing entity IntelliJ Idea doesn't know the entity. So I'm wondering, if the only way is to re-import the table?
BankEntity exists in the JavaProject, but the mapper doesn't recognize it.
Thanks !
If it is an entity that is newly added to the Intellij project, it is unaware of the related table in the database.
You have to Generate Persistence Mapping -> By Database Schema and choose/define the the datasource and then import the table. If the definition of an already mapped entity have been changed(e.g. new column added), then a refresh might help.
I'm going to answer my own question: When generating the entities, Intelli recognizes that there is an existing entity and only add the new attributes to that class. It's somehow confusing, that you have to select the entity like a new entity...but it work's.

JPA2(JBoss7.1's Hibernate) entityManager.find() is getting data from Cache not from DB

I am developing a web application using JSF2, JPA2, EJB3 via JBoss7.1.
I have an Entity(Forum) which contains a list of child entities(Topic).
When I tried to get the list of Topics by forumId for the first time the data is being loaded from DB.
List<Topic> topics = entityManager.find(Forum.class, 1).getTopics();
After that I am adding few more child entities(Topics) to Forum and then again I am trying to retrieve list of Topics by forumId. Nut I am getting the old cached results only. The newly inserted child records are not being loaded from DB.
I am able to load the child entities(Topics) by using following methods:
Method1: Calling entityManager.clear() before entityManager.find()
Method2: Using
em.createQuery("select t from Topic t where t.forum.forumId=?1", Topic.class);
or
em.createQuery("SELECT t FROM Topic t JOIN t.forum f WHERE f.forumId = ?1", Topic.class);
I am aware of setting the QueryHints on NamedQueries. But em.find() method is in a super CrudService which is being extended by all DAOs(Stateless EJBs). So setting QueryHints won't work for me.
So I want to know how can i make em.find() method to load data from DB instead of Cache?
PS: I am using Extended Persistence Context type.
#PersistenceContext(unitName="forum", type=PersistenceContextType.EXTENDED)
protected EntityManager em;
You can specify the behavior of individual find operations by setting additional properties that control the entity managers interaction with the second level cache.
Map<String, Object> props = new HashMap<String, Object>();
props.put("javax.persistence.cache.retrieveMode", CacheRetrieveMode.BYPASS);
entityMgr.find(Forum.class, 1, props).getTopics();
Is it possible that the relation between Forum and Topic was only added in one direction in your entity beans? If you set the forum id on the topic, you should also add this topic to the Forum object to have consistent data inside the first level cache. You should also make sure that you are not using two different entity managers for the update and find. The first level cache is only kept per entity manager, another em can still contain an older version of the entitiy.
Probably unrelated, but with JPA2 you also have a minimal api to evict entities from the second level cache, which could be used after an update:
em.getEntityManagerFactory().getCache().evict(Forum.class, forumId);
Put #Cacheable(false) within the Forum.class.

Categories