How to access EntityManager inside Entity class in EJB3 - ejb-3.0

I need to execute a db query inorder to set the extra column "The order by column" in a many to many association table. so I need to access the db sequence from inside the Entity class and and select the nextval of the sequence and assign it to the order_by column in #prepersist lifecycle callback method.
#Entity
public class ProductWishlist implements Serializable
{
....
#Column(name="ORDER_BIT")
private long orderBit;
// getter setter
// .......
#Prepersist
public void setOrderBit(EntityManager entityManager)
{
Query q=entityManager.createNativeQuery("select nextval('SHP_PRODUCTS_PICS_ORDER_SEQ')");
Long order=(Long)q.getResultList().get(0);
this.setOrderBit(order);
}
}
HOw can I access entitymanger from within setOrderBit ()? how can i Pass Entitymanager into it?
Or How can i execute native query inside an Entity class?

Injecting EntityManager in entity bean isn't recommended. In my view, the entity bean acting as POJO is meant for data transmission between layers or network.
Its better to pre-populate entity, data manipulation prior persistence. But some validation on attributes or formatting of data can be done within entity callback methods.
Here, you can try using entity listener by applying #EntityListeners annotation on entity, which gets notified upon entity lifecycle callback method.

You should not use EntityManager in pre-persist (or in lifecycle methods in general), because it is not aloud according specification:
In general, the lifecycle method of a portable application should not
invoke EntityManager or Query operations, access other entity
instances, or modify relationships within the same persistence
context.[43] A lifecycle callback method may modify thepost persist
non-relationship state of the entity on which it is invoked.
[43] The semantics of such operations may be standardized in a future
release of this specification.
Just obtain normal JDBC connection and use it to execute query.

Related

FindOne brings me the value from the cache memory and not the one from the database in Spring Data

I have a problem with spring data, when executing FindOne when I am updating a product, to compare the value of an attribute with the same one in the database. The FindOne process brings me the same object that I have in memory and not the one from the database, someone knows how I do to bring the one from the database, I know it's because of the hibernate cache, but I can't make it work in repository
#Override
public CuentaDetalle findOne(Long id) {
return cuentaDetalleRepository.getOne(id);
}
It can be done. You should detach the entity.
If everything is well set in your spring-boot project you can easily autowire EntityManager, and then later in your method, you can use entityManager.detach()
#Autowired
private EntityManager entityManager;
...
public someMethod(CuentaDetalle cuentaDetalleToDetach) {
entityManager.detach(cuentaDetalleToDetach);
Then later when using return cuentaDetalleRepository.getOne(id); where id is equal to the cuentaDetalleToDetach.id, the fresh veriosn from db will be returned.
Although I strongly advise using this approach carefully.
There are some drawbacks to detached objects, for example, you cannot use lazy fetch on collection properties (one-to-many).
Then the detached entity will not be saved at the end of the transaction. To save it you should explicitly use cuentaDetalleRepository.save(cuentaDetalleToDetach).
I will stop here, there is a lot to write on this topic.
But I hope, I did answer your question.

Generic Repository in Spring JPA

We are working on a Restful project with lots of DB tables. Though the operations on the tables are almost same and mainly INSERT/UPDATE/DELETE/FETCH.
my questions is:
will we have to create a repository (extending JpaRepository) for every entity (Domain class) we create or, there is an option of creating a GenericRepository that can handle all the above-mentioned functionalities for all the entities?
i.e a single GenericRepository for all.
if so, could you share an example?
is [there] an option of creating a GenericRepository that can handle all the above-mentioned functionalities for all the entities?
You are looking at this with a wrong assumption: You are really not supposed to have a repository per table/entity but per Aggregate(Root). See Are you supposed to have one repository per table in JPA? for more details.
Second: Having a generic repository kind of defies the purpose of Spring Data JPA, after all, JPA already has a generic repository. It's called EntityManager. So if you just need the operations you mentioned, just injecting an EntityManager should be fine. No need to use Spring Data JPA at all. And if you want to have something between your business code and JPA specifics, you can wrap it in a simple repository as described by #AlexSalauyou.
One final point: You'll have the code to create all the tables somewhere. You'll also have the code for all the entities. And you have the code for testing this. Is having a trivial interface definition for each going to be a problem?
For insert/update/delete operations such repository may be as simple as:
#Component
public class CommonRepository {
#PersistenceContext
EntityManager em;
#Transactional
public <E> E insert(E entity) {
em.persist(entity);
return entity;
}
#Transactional
public <E> E update(E entity) {
return em.merge(entity);
}
#Transactional
public void delete(Object entity) {
em.remove(entity);
}
}
For more accurate code, refer SimpleJpaRepository implementation

Spring-boot with eclipseLink transaction issue

I have implemented EntityListener in eclipseLink. My app is built using spring-boot , spring-data and eclipseLink. I have a requirement of inserting record in 2 table (Audit tables) when data in inserted in 1 table. I have got EntityManager in my Listener and everything seems to works normally. When I debug the code I can see that entities which I am going to save are having "id" generated from the sequence which is maintained at DB level. BUT when the transaction is committed I see only main table data but audit tables data is not getting committed. Here is the sample code :
#Entity
#Table(name="MyTable")
#EntityListeners(MyTableListener.class)
public class MyTable implements Serializable{
private static final long serialVersionUID = -5087505622449571373L;
private Long id;
// Rest of the fields
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="MASTER_SEQ")
#SequenceGenerator(name="MASTER_SEQ",sequenceName="MASTER_SEQ",allocationSize=1)
public Long getId() {
return id;
}
// Getters/Setters
}
Listener code :
public class MyTableListener extends DescriptorEventAdapter {
#Override
public void postInsert(DescriptorEvent event) {
MyTable msg = (MyTable)((InsertObjectQuery) event.getQuery()).getObject();
EntityManager em = BeanUtil.getBean(EntityManager.class);
MyAudit statusAudit = new MyAudit();
statusAudit.setNewVal("NEW");
statusAudit.setOldval(null);
statusAudit.setTargetColumnName(targetColumn);
statusAudit.setTargetRecordId(msg.getId());
em.persist(statusAudit);
}
}
Nothing seems to be wrong with the code. BUT when I see in the set the sql logs to "FINEST" , I can see that insert queries are not being fired for audit tables.
I have been dealing with this problem for more than 2 days and dont understand what exactly is wrong. Please help me.
You are never calling flush on the EntityManager. I suspect something like the following is going on:
You add your domain entities to the EntityManager, possibly through Spring Repositories.
Something triggers a flush, possibly the transaction handling of Spring.
Your domain entities get flushed.
Your event listener gets triggered.
You add audit entities to the EntityManager, but those never get flushed.
The database connection gets a commit. Saving everything but your audit trail.
If this theory ist correct, which you should be able to verify through the logs and debugger, you can probably fix it, by adding a call to flush in your listener.
As you described in the comments this causes further problems, which happen because you are trying to do something which you are not supposed to be doing.
According to this article, the JPA spec does not allow usage of the entitymanager inside callback events.
In general, the lifecycle method of a portable application should not invoke EntityManager or Query operations, access other entity instances, or modify relationships within the same persistence context. A lifecycle callback method may modify the non-relationship state of the entity on which it is invoked.
So looks, like we are stuck here, so you probably should consider a completely different approach like EclipseLink History tables.

Not using #Transactional and calling persistence layer methods

I have my services like this annotated with #Transactional
#Transactional
#Service
public class Contact_Service {
....
In my Controller i don't have #Transactional and sometimes i use some persistence layer methods directely to search and persist my object and everything is okey :
#Controller
public class GestionAO {
....
#RequestMapping(...)
public String alerte() {
contact_respository.findOne(...) ;
...
contact_respository.save(...) ;
My question since my controller is not in a transaction will i got some problems ?
Can my object don't get saved in some cases ?
Will i got concurrence problem ?
Looks fine now when you have only one save call. If there are multiple DML or DDL operations executed from the Controller you will lose on not having transaction management. You will lose on the ACID behavior that transactions offer.
The purpose of having #Transactional annotation is to perform multiple database operations in a single transaction. If one operation fails, entire transaction should be roll-backed. Otherwise this can cause data integrity issues.
And the most important thing is to have #Transactional on service layer methods. Because that's one layer who knows unit of work and use cases. In some cases you will have to call multiple DAO layer methods, those all operations are called from service layer and mostly from single method. So it is always better to annotate your service layer methods as #Transactional.
To sum it up, You must have #Transactional annotations on service layer methods.
you should only annotate service with #Transactional. to make sure all of db operations under single transaction, it is recommended to add a new method in service which contains all you db operations, and just call this new method in controller.

How can I test that a JPA save actually saves data?

I am using JPA with Spring and saving an entity in a test. In the process of writing a test to validate that an entity's relationship with another entity is correctly set up, I have come across a problem that I come across frequently. I have a test method (set to rollback) that:
Creates entity
Saves entity
Flushes
Retrieves entity
Validates entity
The problem is that when I look at the Hibernate logs, I only see a single insert to the database where I'd expect to see an insert and then a select.
I know this is because Hibernate's trying to save me some time and knows that it's got the entity with the ID I'm trying to retrieve but that bypasses an important step: I want to make sure that the entity actually made it to the database and looks like what I thought it should. What's the best way to deal with this so I can test that the entity is actually in the database?
Note: I assume this involves somehow detaching the entity or telling Hibernate to clear its cache but I'm not sure how to do that when all I have access to is a JpaRepository object.
Some code:
public interface UserRepository extends JpaRepository<User, Long> {
//...
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = JpaConfig.class, // JpaConfig just loads our config stuff
loader = AnnotationConfigContextLoader.class)
#TransactionConfiguration(defaultRollback = true)
public class UserRepositoryTest {
#Test
#Transactional
public void testRoles() {
User user = new User("name", "email#email.com");
// eventually more here to test entity-to-entity relationship
User savedUser = userRepository.save(user);
userRepository.flush();
savedUser = userRepository.findOne(savedUser.getId());
Assert.assertNotNull(savedUser);
// more validation here
}
}
You basically want to test Hibernate's functionality instead of your own code. My first suggestion: don't do it! It is already tested and validated many times.
If you really want to test it, there are a couple of options:
Execute a query (rather than a get. The query will get executed (you should see it in the log) and the result interpreted. The object you get back would still be the same object you saved, since that is in the session.
You can evict the object from the session and then get it again. If you use SessionFactory.getCurrentSession(), you'll get the same season that the repository is using. With that you can evict the object.
You have two strategies:
issue a native SQL query therefor bypassing any JPA cache.
ensure the persistence context is cleared before reloading.
For (1) you can change your tests to extend the following Spring class which, in addition to automatically beginning/rolling back a transaction at the start/end of each test, will give you access to a Spring JdbcTemplate you can use to issue the native SQL.
http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.html
http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/jdbc/core/simple/SimpleJdbcTemplate.html
For (2) you can clear the persistence context by doing the following (where the EntityManagerFactory is injected into your test:
EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory).clear();
See the following base test class which I normally use and demonstrates the above and also allows for populating the database with known data before each test (via DBUnit).
https://github.com/alanhay/spring-data-jpa-bootstrap/blob/master/src/test/java/uk/co/certait/spring/data/repository/AbstractBaseDatabaseTest.java
(In fact in the above I am actually creating a new JdbcTemplate by injecting a datasource. Can't remember why...)

Resources