I am making an updating native query within my Spring Data JPA Repository.
#Query(nativeQuery=true, value="update t_table set change = 0 where id = ?1")
#Modifying(clearAutomatically=false)
void disableChange(int id);
The value is updated in a correct way as I can see with my database client but next transactions are not recognizing the change until the server is redeployed.
My service looks like this:
#Service("my.service.class.service")
public final class MyServiceClassImpl implements MyServiceClass
{
#Autowired
private ClientRepository clientRepository;
#Override
#Secured("MY_ROLE")
#Transactional(propagation=Propagation.REQUIRES_NEW, rollbackFor=MyException.class)
public void myMethod() throws PlayTheGuruException
{
//
myMethod();
//
}
private void myMethod() throws MyException {
//
clientRepository.disableChange(22);
//
}
}
May I need to annotate myMethod with transactional and mandatory propagation or something like that?
Is the native query the problem?
This issue appears only with updating queries.
Looks like a stale value from L2 cache, if L2 cache is enabled for the entity in question.
Try to remove affected entity from L2 cache manually:
#Autowired EntityManagerFactory emf;
...
clientRepository.disableChange(22);
emf.getCache().evict(..., 22);
Related
I have a service class like this.
#Service
public class ServiceAImpl implements ServiceA {
#Autowired
private ServiceARepository repo;
#Autowired
private Publisher publisher;
#Transactional
public String save(Model model) {
//entity list creation code goes here
List<EntityA> entityList = repo.save(entityList);
repo.flush();
...
//savedIdList is comma separated list of entityList EntityA.id
publisher.publish(savedIdList);
return responseObject.toString();
}
}
When controller call to this service its create the Entity list and save. After that its call to publish method in another class with the saved ids. This save method annotated with #Transactional.
#Service
public class Publisher {
#Autowired
private AsyncPublisher asyPublisher;
#Autowired
PublishedDataRepository repo;
public void publish(String savedIdList) throws Exception {
savePublishData(..., savedIdList);
}
#Transactional
private void savePublishData(..., String savedIdList) throws Exception {
SearchPublishedData publishData = new SearchPublishedData();
...
publishData.setIdList(savedIdList);
publishData = repo.saveAndFlush(publishData);
asyPublisher.callPublisher(..., savedIdList, publishData.getId());
}
}
In publisher class its save a record to the publisher table and again it call to the async publisher class. In this class there is a method with #Async and its implemented with ThreadPoolTaskExecutor. In this async method what it going to do is get the previously saved data from its ids using EntityManager native query.
This is a sample java class code. Actually in this native query there are few tables join with this previously saved table and getting the data.
#Service
public class AsyncPublisher {
#PersistenceContext
private EntityManager entityManager;
#Async("threadPoolTaskExecutor") //This is created as ThreadPoolTaskExecutor
public void callPublisher(..., String ids, Long savedId) {
try {
String query = getQuery(ids);
List<Object[]> results = entityManager.createNativeQuery(query).getResultList();
... ///Other codes goes here, but results is empty
} catch (Exception e) {
logg error
}
}
private String getQuery(String ids) throws Exception {
StringBuilder query = new StringBuilder();
query.append("SELECT * FROM EntityA_table WHERE id IN (").append(ids).append(" ) ");
//This is a sample query, actually in this query there are 2 or more tables joined
return query.toString();
}
}
My problem is when I retrieve data from EntityManager native query time to time its not getting the data. But when I check the database with those ids those data available in database.
Anyone can give me the reason for this.
I think this saving block is annotated with #Transactional and it going to commit data to the Database at the end of the method execution and but before it save to the database EntityManager native query execution happens in another thread and its execute before the data commit. Is this correct? If not please explain someone what is happening here.
Also is there any way to avoid this data not retrieve scenario?
Thank you.
I am sure that I am missing something, but I don't know exactly what...
Giving the following snippet:
#Service
public class MyClass {
private MyClass self;
private UserRepository userRepository;
private ApplicationContext applicationContext;
#PostConstruct
private void init() {
self = applicationContext.getBean(MyClass.class);
}
#Transactional
public void doA(User user) {
...
if (condition) {
self.doB(user);
throw new SecurityException();
}
user.setRandomField("x");
userRepository.save(user);
}
#Transactional(value = Transactional.TxType.REQUIRES_NEW)
public void doB(User user) {
...
userRepository.save(user);
}
}
What do I know about #Transactional is that if it is used, is redundant to call repository.save(entity).
What I am trying to do, is to process an entity from a transactional method, and if there is a breaking condition, call a new method (annotated with REQUIRES_NEW) that will update some fields of the entity and save it. The root method (doA) then throws an exception. FYI: the #Transactional(dontRollbackOn = SecurityException.class) is not an option in this situation.
For using this commiting mechanism, instead of creating a new bean just with one method I just injected the current bean into a variable just called self, therefore I can use the bean proxy for transaction management.
The odd thing is that if I am removing from doB the save call, when doA transaction is rollbacked because of the SecurityException, the changes performed by doB are rollbacked as well. But if I let it in there, this is working as expected.
Am I doing something wrong or am I missing something?
Thanks!
Try to do not pass User instance in the doB().
Pass an Id instead and read the User from the repo internally. I am not sure how the attached entity is handled between the different sessions.
I am inserting the data in one method(has #Transactional(propagation = Propagation.Required)) but in the other method(has #Transactional(propagation = Propagation.Required)) if I try to get the same data it is giving null.
Both methods are wrote in service layer with #Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
How to get the data which is inserted in the same transaction.
Something like :-
#Service
public class Service{
#Transactional
public void method(){
mapper.insert(); //insert to DB(Using Mapper interface)
ServiceLayer.method2()
}
}
#Service
public void ServiceLayer{
#Transactional
public static void method2(){
result = mapper.select() //Select inserted data - returning null
}
}
In order to persist the changes made to the current session you can invoke entityManager.flush();
It may be worked, but it's not a solution.
In your case, your Transaction from Service.method() created a transaction that is not committed yet. That's why you can't fetch it.
I found the answer...after removing #transactional from ServiceLayer.method2() it's worked fine.
What I want is to implement the Repository pattern in a JPA/Hibernate application. I have a generic interface that describes the basic contract of my repositories:
public interface EntityRepository<Entity extends Object, EntityId> {
Entity add(Entity entity);
Entity byId(EntityId id);
void remove(Entity entity);
void removeById(EntityId id);
void save();
List<Entity> toList();
}
And here is an implementation of such an interface:
public class EntityRepositoryHibernate<Entity extends Object, EntityId>
implements Serializable,
EntityRepository<Entity, EntityId> {
private static final long serialVersionUID = 1L;
#Inject
protected EntityManager entityManager;
protected Class<Entity> entityClass;
public EntityRepositoryHibernate(Class<Entity> entityClass) {
this.entityClass = entityClass;
}
public EntityManager getEntityManager() {
return entityManager;
}
#Override
public Entity add(Entity entity) {
entityManager.persist(entity);
return entity;
}
#SuppressWarnings("unchecked")
#Override
public Entity byId(EntityId id) {
DetachedCriteria criteria = criteriaDAO.createDetachedCriteria(entityClass);
criteria.add(Restrictions.eq("id", id));
return (Entity)criteriaDAO.executeCriteriaUniqueResult(criteria);
}
#Override
public void remove(Entity entity) {
if(entity==null)
return;
entityManager.remove(entity);
}
#Override
public void removeById(EntityId id) {
remove(byId(id));
}
#Override
public List<Entity> toList() {
throw new UnsupportedOperationException("toList() not implemented in "+entityClass.getName());
}
#Override
public void save() {
entityManager.flush();
}
}
All methods are working fine, except save(), so this is the focus here.
As far as I understand, Hibernate is able to track all changes in any instance returned by a query (the byId() method). So, the idea of the save() method is to save any instances that where retrieved and changed, that's why the method does not receives any parameters, it is supposed to save everything that has to be saved (which means, any persistent instance that was retrived and somehow updated while the repository lives.
In a possible scenario, I could call byId() 10 times to retrieve 10 different instances and change only 4 of them. The idea is that by calling save() once, those 4 instances would be saved in the data server.
Problem is when I call flush() I receive an exception stating that there is no transaction active. Since I'm using a JTA persistence unit, it's illegal to open the transation programatically by calling entityManager.getTransaction().
Considering that, what to do to fix the code?
First of all, it seems that your are missunderstanding the purpose of EntityManager.flush method. It doesn't commit any changes managed by persistence context, just sends SQL instructuions to the database. I mean, for the same JTA transaction, when you retrieve and modify some entity instances the changes/SQL instructions are cached waiting to be sent to the database. If the underlying transaction is commited this changes are flushed to the database along with the commit instruction. If you invoke flush before transaction is commited, only flush the changes until the invokation point (well, some SQL instruction could have been flushed previously by reasons out of this matter) but not the commit instruction is sent.
How to fixed?
I suggest you to don't mix Repository Pattern with Transaction manipulation.
Looks like you are using Container Managed Transactions (javaee tutorial) so just erase the save method and let container to manage the transactions. This will change your focus, you now have to care about rolling back transactions (throwing exception or invoking setRollbackOnly) but you don't need to explicit commmit.
I'm working on project where I use mainly EJB and JPA, but I have problem with ConstraintViolationException that should not happen.
First of all I have MyEntity class with #Id and few unique fields.
I have #Stateless MyEntityRepository class with find() method which just returns MyEntity (or null) by calling EntityManager get() method.
Another #Stateless bean is SaveEntityBean:
#Stateless
public class SaveEntityBean {
#Inject
EntityManager em;
#Inject
MyEntityRepository repository;
public void saveEntity(MyEntity me) {
if(repository.find(me) == null) {
//the place with ConstraintViolationException
em.persist(me);
}
}
public void saveEntities(List<...> entities) {
for(MyEntity me: entities)
saveEntity(e);
}
}
and the method saveEntities(List<...> entities) is called from another bean:
#Stateless
#Startup
public class SaveEntityBean {
#Inject
SaveEntityBean saveBean;
//...
#Schedule(hour = "*", minute = "*", second="*/5")
#AccessTimeout(unit = TimeUnit.MINUTES, value = 1)
public void mainLogicMethod() {
List<MyEntity> entities = io.calculateAndGetEntities();
saveBean.saveEntities(entities);
}
}
where the io.calculateAndGetEntities() method is long IO work. The problem is that sometimes I get org.hibernate.exception.ConstraintViolationException which in my opinion should not occur, because I check MyEntityRepository.find(me) != null condition before calling persist() method.
The only idea I have is that there is some delay between commits, so after calling MyEntityRepository.find(me) method in condition check, the commit occurs, and just after that the persist() method throws the exception.
Please give me any suggestions what to read and learn and how to solve the problem.
edit:
I found that it is a problem with threading, so probably the solution is with locking write/read.
Ok, generally in a high concurrent enviroment it is really hard to solve this problem and simply Locking will not work(and You don't want to use table locks). For now I use Hibernate #SQLInsert annotation from org.hibernate.annotations.SQLInsert so my Entity looks like this:
#SQLInsert(sql = "INSERT INTO my_entity_table(a, b, c) VALUES(?, ?, ?) ON DUPLICATE KEY UPDATE a=a;")
public class MyEntity implements Serializable {...}
Of course it will be problematic, when You would like to show some error message to the user, but in my case it is good enough.
As far as I know this SQL statement is supported only in MySQL, in other RDBMS you should use different aproach.