Call to detach method is not removing entity from persistence context - spring-data-jpa

My environment
Java 8/Spring Data JPA 2.0.5/JPA 2.1/Eclipse Link 2.6.4/JBoss EAP 7.1
My scenario
I have three entities (#Entity): Folder, Configuration and Document.
I have three repositories (#Repository): FolderRepository, ConfigurationRepository and DocumentRepository.
I have one service (#Service): TestService.
Transactionality is only defined in TestService methods.
My Problem
If we execute 'test' method, code try to persist a 'Folder' entity in database:
If entity doesn't exist in database, then is persisted and the call to 'documentRepository.findById' works properly.
If entity already exists in database, a PersistenceException is thrown. We catch the exception and detach the entity. When we call to
'documentRepository.findById' a SQLIntegrityConstraintViolationException is thrown. Why?
I don't understand why this exception in thrown if we have detached the entity. This entity should not be in the persistence context.
My code
#Service
public class TestServiceImpl implements TestService
{
#Autowired
private FolderRepository folderRepository;
#Autowired
private ConfigurationRepository configurationRepository;
#Autowired
private DocumentRepository documentRepository;
#PersistenceContext
private EntityManager em;
#Override
#Transactional(readOnly = false)
public void test(String documentCode)
{
List<Configuration> configuration = configurationRepository.findAll();
DateFormat dateFormat = new SimpleDateFormat("yyyyMM");
Date date = new Date();
String folderName = dateFormat.format(date)
Folder folder = new Folder ();
folder.setName (folderName); //Primary key
folder.setDateCreation (date);
// We try to persist 'folder' entity using an EntityManager instance.
// We don't use 'saveAndFlush' because we don't want a merge happens.
// If constraint exception is thrown, we detach the 'folder' entity
try
{
em.persist(folder);
em.flush();
}
catch (PersistenceException e)
{
em.detach(folder);
}
// Why is a SQLIntegrityConstraintViolationException thrown here?
folder = documentRepository.findById (documentCode);
............ more code ............
}
}

Related

How not to rollback #DataJpaTest?

In the following code
#DataJpaTest
#Transactional(propagation = Propagation.NOT_SUPPORTED)
#AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE)
public class GenreDaoJpaTest{
#Autowired
private TestEntityManager entityManager;
#Autowired
private GenreRepository dao;
....
}
when I'm adding #Transactional(propagation = Propagation.NOT_SUPPORTED) with the purpose to cancel a roolback after each test I'm getting an exception:
ava.lang.IllegalStateException: No transactional EntityManager found
at org.springframework.util.Assert.state(Assert.java:73)
at org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager.getEntityManager(TestEntityManager.java:237)
at org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager.persist(TestEntityManager.java:92)
at ru.otus.ea.dao.GenreDaoJpaTest.init(GenreDaoJpaTest.java:38)
It there a way to autowire TestEntityManager and not to roolback transactions in tests?
Your TestEntityManager is autowired but you are executing the persist call outside of a transaction.
You can autowire TransactionTemplate:
#Autowired
private TransactionTemplate transactionTemplate;
And execute your DB interactions using its execute method:
User savedUser = transactionTemplate.execute((conn) -> {
return testEntityManager.persist(new User("foo"));
});
Also you should be aware that now you are responsible for cleanup of test DB after tests execute (which might be hard to maintain as logic grows):
#BeforeEach // just to be sure
#AfterEach
public void cleanup() {
userRepository.deleteAll();
}

How do I create a separate entity manager for bulk operations in a JTA environment?

In JPA, when doing bulk operations such as this
update LogEntry e set e.customer = null where e.customer.id = :cid
It is recommended to use a separate entity manager to avoid breaking synchronization, according to this: UPDATE SET Queries in JPA/JPQL
For example, the EntityManager may not be aware that a cached entity object in its persistence context has been modified by an UPDATE query. Therefore, it is a good practice to use a separate EntityManager for UPDATE queries.
How do I create a separate entity manager in a JTA environment such as Wildfly using hibernate? Do I need to create a separate persistence unit for bulk operations?
EDIT: Given I dont need a separate PU for bulk operations, is this a sufficient way of solving it using a new transaction?
#Transactional
public class JpaCustomerRepository implements CustomerRepository {
#Inject
private EntityManager em;
...
#Override
public Customer remove(long id) {
CustomerEntity entity = em.find(CustomerEntity.class, id);
if (entity != null) {
updateLogEntriesToNull(entity);
em.remove(entity);
return entity;
} else {
return null;
}
}
#Transactional(value=TxType.REQUIRES_NEW)
public void updateLogEntriesToNull(CustomerEntity entity) {
em.createNamedQuery(LogEntry.updateCustomerToNull)
.setParameter("cid", entity.getId())
.executeUpdate();
}
...
}
Where LogEntry.updateCustomerToNull is the bulk query.
Answer: This does not work because the interceptor is not invoked when called from inside the same class.
EDIT2: Following the suggestions from Andrei, this should work:
#Transactional
public class JpaCustomerRepository implements CustomerRepository {
public static class BulkUpdater {
#Inject
private EntityManager em;
#Transactional(value=TxType.REQUIRES_NEW)
public void updateLogEntriesToNull(CustomerEntity entity) {
em.createNamedQuery(LogEntry.updateCustomerToNull)
.setParameter("cid", entity.getId())
.executeUpdate();
}
}
#Inject
private EntityManager em;
#Inject
private BulkUpdater bulkUpdater;
...
#Override
public Customer remove(long id) {
CustomerEntity entity = em.find(CustomerEntity.class, id);
if (entity != null) {
bulkUpdater.updateLogEntriesToNull(entity);
em.remove(entity);
return entity;
} else {
return null;
}
}
...
}
Testing confirms that the interceptor gets called twice.
The recommendation is valid only if you also do other stuff with the EntityManager (when there is a risk of manipulating/reading the same entities as the BULK UPDATE). The easiest solution: make sure that this BULK UPDATE is executed in a separate service, within a new transaction. No need to create a separate PU (persistence unit) for bulk operations.

#ManyToOne(fetch=FetchType.LAZY) lazy loading not working

I am working with JPA 2.1 (EclipseLink 2.5.1) and JBoss 7.1.
I've define very simple JPA entities:
#Entity
#Table(name="APLICACIONES_TB")
public class Aplicacion implements Serializable {
#Id
#Column(name="COD_APLICACION_V")
private long codAplicacionV;
#Column(name="APLICACION_V")
private String aplicacionV;
#OneToMany(mappedBy="aplicacion")
private Collection<Prestacion> prestaciones;
... getters and setters
}
#Entity
#Table(name="PRESTACIONES_TB")
public class Prestacion implements Serializable {
#Id
#Column(name="COD_PRESTACIONES_V")
private String codPrestacionesV;
#Column(name="DESCRIPCION_V")
private String descripcionV;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "COD_APLICACION_V")
private Aplicacion aplicacion;
... getters and setters ...
}
I have developed a staless EJB that executes a query to obtain some "Aplicacion" entities.
#Stateless
#LocalBean
public class DocuEJB implements DocuEJBLocal
{
#PersistenceContext(name="DocuEjb", type=PersistenceContextType.TRANSACTION)
private EntityManager em;
public Prestacion getResult(String name)
{
return em.createNamedQuery("ExampleQueryName", Prestacion.class).getSingleResult();
}
}
Because I'm working with JSF 2.1 the EJB is being injected in a managed bean:
#ManagedBean(name = "ManagedBean")
#RequestScoped
public class ManagedBean
{
#EJB DocuEJB docuEjb;
public String doSomething()
{
Prestacion entity = docuEjb.getResult("egesr");
if (entity != null)
{
// It should return null because 'entity' should be detached
Aplicacion app = entity.getAplicacion();
// but 'app' entity is not null, ¿why not?
System.out.println (app.getCodAplicacionV());
}
}
}
Lazy loading is not working even when lazy loading has been defined for 'aplicacion' field on 'Prestacion' entity. The code posted before should return a NullPointerException in the next line:
System.out.println (app.getCodAplicacionV());
because 'app' entity is detached and lazy loading has been configured.
Why is not working lazy loading?
Thanks
Try to add #Transactional on doSomething(), I think that your transaction manager is not well configured.
You can see here the official spring documentation. In any case, can you add your spring configurations, so that we can better help you. :)
I don't think the behavior your encounter is abnormal or your question should state it clearly:
EJB are by default transactional
Your JSF inject an EJB, with #EJB, and I guess JBoss can create a java reference and not a proxy
The entity is being managed because the transaction is not done, it will finish when doSomething ends.
Your entity is then loaded into the EntityManager, and lazy loading works because there is a context to it.
You would call em.evict(entity) with the result your are getting, this would probably fails because the entity would not be managed any more.

How to get entityManager by using an entity class

How to reach the entity manager which managed the entity. I mean; suppose that i have an entity reference in the sessionBean, how can i get entityManager of this entity belonged one?
I had already tried (plz see getEntityManagerOfEntity() method) contains method of em; but it does not work.
Thx
bgrds
#Stateless(name = "MainManager", mappedName = "MainManager")
#TransactionManagement(TransactionManagementType.CONTAINER)
#Interceptors(value = { PerformanceMonitor.class, ProfileInterceptor.class })
public class MainManagerBean implements MainManager, MainManagerLocal
{
private Logger logger = Logger.getLogger(this.getClass());
#PersistenceContext(unitName = "DSApp")
private EntityManager manager;
#PersistenceContext(unitName = "DSIX")
private EntityManager integrationManager;
#Resource
SessionContext ctx;
public EntityManager getEntityManagerOfEntity(SuperEntity superEntity)
{
if (manager.contains(superEntity))
return manager;
else if (integrationManager.contains(superEntity))
return integrationManager;
return null;
}
public SuperEntity findByPrimaryKey(SuperEntity superEntity)
{
getEntityManagerOfEntity(superEntity).setFlushMode(FlushModeType.COMMIT);
return dao.findByPrimaryKey(getEntityManagerOfEntity(superEntity), superEntity);
You cannot backtrack the EntityManager from an entity using the JPA API, even when it is still managed.
What you can do, if you have references to different EMs in your bean and the entity is managed, is to check the right EM by calling em.contains(entity).
In most cases it is not really important to know, which EM has fetched an entity originally, since you can merge the entity into any persistence context and continue working with it.

JAXB eagerly fetches fields marked FetchType.LAZY even when EntityManager has been cleared(), closed() and set to null

I bow to the apt stackoverflow community and humbly seek guidance (I bow my head in subservience as I write this)
I have the following Entity class/bean which has a mixture of JPA/EclipseLink/JAXB/Moxy annotations: (btw EventBase is just a #MappedSuperclass that holds additional fields)
#Entity
#Table(name = "EVENTS")
#XmlRootElement
public class Event extends EventBase {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#XmlAttribute(name = "id")
private long eventCID;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "APPLICATIONCID")
private CustomerApplication customerApplication;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CUSTOMERCID")
private Customer customer;
....
}
Here's my code to marshal this entity (outer class excluded for brevity)
public static void main(String args[]) {
Event event = myInstance.populateEvent();
myInstance.buildXMLFromEvent(event);
}
public Event populateEvent() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory(this.persistenceUnit);
EntityManager em = null;
Event event = null;
try {
em = emf.createEntityManager();
event = (Event) em.createQuery("Select object(e) from Event e where e.eventCID = 55000").getSingleResult();
em.clear();
em.detach(event);
em.close();
em = null;
emf.close();
emf = null;
} catch (Exception e) { // just test code so catching general exception
log.error("Unexpected error: " + e);
} finally {
if (em != null) {
em.clear();
em.close();
}
}
return event;
}
private void buildXMLFromEvent(Event event) {
System.out.println("Marshalling now:");
JAXBContext jc;
try {
jc = JAXBContext.newInstance(Event.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.valueOf(true));
JAXBElement<Event> jaxbElement = new JAXBElement<Event>(new QName("event"), Event.class, event);
marshaller.marshal(jaxbElement, System.out);
} catch (JAXBException e) {
}
}
The generated xml actually goes and eagerly fetches all of the member objects of my Event entity bean! i.e) Customer, CustomerApplication and any other mappings which I've excluded for brevity. I'm using EclipseLink as my JPA provider and Moxy for JAXB. What am I doing wrong here? You can see that not only are the entityManager AND entityManagerFactory instances cleared, closed and set to null, but I've also gone ahead and detached the root Event entity. Moreover, the fetchtype has explicitly been set to LAZY!
How is it that JAXB can eagerly fetch when the Event object has been detached? I thought closing the entityManager in itself detaches all managed objects? Is there some cached session context that JAXB is hanging on to? If so, why is it not even honoring the explicitly defined fetch strategy?
Many thanks in advance!
Ustad
If eclipselink is in standalone mode, then only ManyToMany and OneToMany relations is actually uses lazy-loading, for others fetch attribute is ignored and equal to EAGER.
Here is documentation for that http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Mapping/Basic_Mappings/Lazy_Basics
One-to-one: By default, EclipseLink JPA ignores the fetch attribute and
default javax.persistence.FetchType.EAGER applies.
Many-to-one: EclipseLink JPA performs lazy loading when the fetch
attribute is set to javax.persistence.FetchType.LAZY.
Basic: By default, EclipseLink JPA ignores the fetch attribute and
default javax.persistence.FetchType.EAGER applies.
That`s why your entities are loaded with relations.
Hope it helps.