I have two services, like this (simplified code):
#Service
public class OuterService {
#Autowired
InnerService innerService;
#Transactional
public void doSomething() {
List<SomeEntity> list = entityRepo.findByWhatever(...);
for(SomeEntity listElement : list) {
innerService.processEntity(listElement);
}
}
}
#Service
public class InnerService {
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void processEntity(Entity entity) {
// ...
StatusElement status = new StatusElement(...);
statusElementRepo.save(status);
}
}
The constructed StatusElement is now inserted by exiting InnerService.processEntity() and inserted again by exiting OuterService.doSomething().
If I change the #Transactional annotation of OuterService.doSomething() to #Transactional(readOnly = true), it is inserted just once.
Is it a problem with MySql (because it may not support nested transactions), do I need a special transaction manager, or is there something wrong with my code? TIA!
I solved it by using programmatically transactions using the PlatformTransactionManager.
see: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html#transaction-programmatic-ptm
Related
I'm using Spring Rest. I have an Entity called Operator that goes like this:
#Entity
#Table(name = "operators")
public class Operator {
//various properties
private List<OperatorRole> operatorRoles;
//various getters and setters
#LazyCollection(LazyCollectionOption.TRUE)
#OneToMany(mappedBy = "operator", cascade = CascadeType.ALL)
public List<OperatorRole> getOperatorRoles() {
return operatorRoles;
}
public void setOperatorRoles(List<OperatorRole> operatorRoles) {
this.operatorRoles = operatorRoles;
}
}
I also have the corresponding OperatorRepository extends JpaRepository
I defined a controller that exposes this API:
#RestController
#RequestMapping("/api/operators")
public class OperatorController{
private final OperatorRepository operatorRepository;
#Autowired
public OperatorController(OperatorRepository operatorRepository) {
this.operatorRepository = operatorRepository;
}
#GetMapping(value = "/myApi")
#Transactional(readOnly = true)
public MyResponseBody myApi(#ApiIgnore #AuthorizedConsumer Operator operator){
if(operator.getOperatorRoles()!=null) {
for (OperatorRole current : operator.getOperatorRoles()) {
//do things
}
}
}
}
This used to work before I made the OperatorRoles list lazy; now if I try to iterate through the list it throws LazyInitializationException.
The Operator parameter is fetched from the DB by a filter that extends Spring's BasicAuthenticationFilter, and is then somehow autowired into the API call.
I can get other, non-lazy initialized, properties without problem. If i do something like operator = operatorRepository.getOne(operator.getId());, everything works, but I would need to change this in too many points in the code.
From what I understand, the problem is that the session used to fetch the Operator in the BasicAuthenticationFilter is no longer open by the time i reach the actual API in OperatorController.
I managed to wrap everything in a OpenSessionInViewFilter, but it still doesn't work.
Anyone has any ideas?
I was having this very same problem for a long time and was using FetchType.EAGER but today something has clicked in my head ...
#Transactional didn't work so I thought "if declarative transactions don't work? Maybe programmatically do" And they do!
Based on Spring Programmatic Transactions docs:
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
private final TransactionTemplate transactionTemplate;
public JwtAuthorizationFilter(AuthenticationManager authenticationManager,
PlatformTransactionManager transactionManager) {
super(authenticationManager);
this.transactionTemplate = new TransactionTemplate(transactionManager);
// Set your desired propagation behavior, isolation level, readOnly, etc.
this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
}
private void doSomething() {
transactionTemplate.execute(transactionStatus -> {
// execute your queries
});
}
}
It could be late for you, but I hope it helps others.
I have a simple JpaRepository annotated with #RepositoryRestResource:
#RepositoryRestResource
public interface ItemRepository extends JpaRepository<Item, UUID> { }
Whenever something is changed in the database, I want to update a file. I do this using a RepositoryEventHandler:
#Component
#RepositoryEventHandler
public class ItemRepositoryEventHandler {
#HandleAfterCreate
#HandleAfterSave
#HandleAfterDelete
public void itemChanged(Item item) {
writeToFile();
}
}
What I want to do is if there is an error while writing the contents to file, then the database should be rolled back.
I've tried by adding the #Transactional annotation to the ItemRepository but it didn't work. Debugging revealed that the RepositoryRestResource does three steps: emitting the BeforeXXX events, persisting to the database, then emitting the AfterXXX events. It only uses a transaction during the persistence step, not one across all three.
So I see no way to use a transaction across the whole operation and the only alternative I see is to not use #RepositoryRestResource, but to implement the web layer manually then use a service which employs a transaction across both repositories. Is there an easier way?
One approach is to implement your business logic with a custom controller and a service. But this way is neutralizing the 'advantages' of Spring Data REST.
Another option (in my opinion it's more natural for SDR) is to use published events from aggregate roots. In this case you should extend your entities from AbstractAggregateRoot and implement a method that will be publish some 'event'. Then you will be able to handle this event (with help of #EventListener) in the same transaction during the process of saving your entity. For example:
#Entity
public class Order extends AbstractAggregateRoot {
//...
public void registerItems(List<Item> items) {
this.registerEvent(new RegisterItemsEvent(this, items));
}
}
#Getter
#RequiredArgsConstructor
public class RegisterItemsEvent {
private final Order order;
private final List<Item> items;
}
#RequiredArgsConstructor
#Component
public class EventHandler {
private final ItemRepo itemRepo;
#EventListener
#Transactional(propagation = MANDATORY)
public void handleRegisterItemsEvent(RegisterItemsEvent e) {
Order order = e.getOrder();
List<Item> items = e.getItems();
// update items with order - skipped...
itemRepo.saveAll(items);
}
}
Usage example:
#Component
#RepositoryEventHandler
public class OrderEventHandler {
#BeforeCreate
public void handleOrderCreate(Order order) {
// prepare a List of items - skipped...
order.registerItems(items);
}
}
When SDR saves the Order then it emits RegisterItemsEvent, which is handled in handleRegisterItemsEvent method of your EventHandler that saves prepared items in the same transaction (we use propagation = MANDATORY parameter of #Transaction annotation to make sure that transaction is present).
Additional info: Domain event publication from aggregate roots
UPDATED
Regarding your particular task, you can create class ItemChangedEvent:
#Getter
#RequiredArgsConstructor
public class ItemChangedEvent {
private final Item item;
}
Implement method markAsChanged in the Item entity:
#Entity
public class Item extends AbstractAggregateRoot {
//...
public void markAsChanged() {
this.registerEvent(new ItemChangedEvent(this));
}
}
When the item changes, you will mark it as "changed":
#Component
#RepositoryEventHandler
public class ItemRepositoryEventHandler {
#BeforeCreate
#BeforeSave
#BeforeDelete
public void itemChanged(Item item) {
item.markAsChanged();
}
}
And write it to a file in the ItemChangedEvent handler in the same transaction:
#Component
public class EventHandler {
#EventListener
#Transactional(propagation = MANDATORY)
public void handleItemChangedEvent(ItemChangedEvent e) {
Item item = e.getItem();
writeToFile(item);
}
}
Consider the following situation:
We receive a request from a web service which updates our entity. Sometimes we might get two requests at (almost) the same time. We had situations in which our entity looked completely wrong, because of concurrent updates. The idea is to lock the entity pessimistic so that whenever the first request comes it instantly locks the entity and the second request can't touch it (Optimistic locking is no alternative for us). I wrote an integration test to check this behaviour.
I got an integration test which looks like the following:
protected static TestRemoteFacade testFacade;
#BeforeClass
public static void setup() {
testFacade = BeanLocator.lookupRemote(TestRemoteFacade.class, TestRemoteFacade.REMOTE_JNDI_NAME, TestRemoteFacade.NAMESPACE);
}
#Test
public void testPessimisticLock() throws Exception {
testFacade.readPessimisticTwice();
}
which calls the bean
#Stateless
#Clustered
#SecurityDomain("myDomain")
#RolesAllowed({ Roles.ACCESS })
public class TestFacadeBean extends FacadeBean implements TestRemoteFacade {
#EJB
private FiolaProduktLocalFacade produkt;
#Override
public void readPessimisticTwice() {
produkt.readPessimisticTwice();
}
}
with produkt being a bean itself
#Stateless
#Clustered
#SecurityDomain("myDomain")
#RolesAllowed({ Roles.ACCESS })
public class ProduktFacadeBean implements ProduktLocalFacade {
#Override
public void readPessimisticTwice() {
EntityManager entityManager = MyService.getCrudService().getEntityManager();
System.out.println("Before first try.");
entityManager.find(MyEntity.class, 1, LockModeType.PESSIMISTIC_WRITE);
System.out.println("Before second try.");
entityManager.find(MyEntity.class, 1, LockModeType.PESSIMISTIC_WRITE);
System.out.println("After second try.");
}
}
with
public class MyService {
public static CrudServiceLocalFacade getCrudService() {
return CrudServiceLookup.getCrudService();
}
}
public final class CrudServiceLookup {
private static CrudServiceLocalFacade crudService;
private CrudServiceLookup(){
}
public static CrudServiceLocalFacade getCrudService() {
if (crudService == null)
crudService = BeanLocator.lookup(CrudServiceLocalFacade.class, CrudServiceLocalFacade.LOCAL_JNDI_NAME);
return crudService;
}
public static void setCrudService(CrudServiceLocalFacade crudService) {
CrudServiceLookup.crudService = crudService;
}
}
#Stateless
#Local(CrudServiceLocalFacade.class)
#TransactionAttribute(TransactionAttributeType.MANDATORY)
#Interceptors(OracleDataBaseInterceptor.class)
public class CrudServiceFacadeBean implements CrudServiceLocalFacade {
private EntityManager em;
#Override
#PersistenceContext(unitName = "persistence_unit")
public void setEntityManager(EntityManager entityManager) {
em = entityManager;
}
#Override
public EntityManager getEntityManager() {
return em;
}
}
The problem that arises now is: If I start the integration test once with a breakpoint at System.out.println("Before second try."); and then start the integration test a second time, the latter one can still read MyEntity. Remarkable is that they were different instances (I made this observation on the instanceId in debug mode). This suggests that the entityManager didn't share his hibernate context.
I made the following observations:
Whenever I call a setter on entity and save it to the db, the lock is aquired. But this is not what I need. I need the lock without having modified the entity.
I tried the method entityManager.lock(entity, LockModeType.PESSIMISTIC_WRITE) as well, but the behaviour was the same.
I found Transaction settings in DBVisualizer. At the moment it is set to TRANSACTION_NONE. I tried all the others (TRANSACTION_READ_UNCOMMITTED, TRANSACTION_READ_COMMITTED, TRANSACTION_REPEATABLE_READ, TRANSACTION_SERIALIZABLE) as well, without any success.
Let the first thread read the entity, then the second thread read the same entity. Let the first tread modify the entity and then the second modify it. Then let both save the entity and whoever saves the entity last wins and no exceptions will be thrown.
How can I read an object pessimistic, that means: Whenever I load an entity from the db I want it to be locked immediately (even if there was no modification).
Both ways you describe ie.
em.find(MyEntity.class, 1, LockModeType.PESSIMISTIC_WRITE)
em.lock(entity, LockModeType.PESSIMISTIC_WRITE)
hold a lock on the related row in database but only for the the entityManager lifespan, ie. for the time of the enclosing transaction, the lock will be so automatically released once you've reached the end of the transaction
#Transactional()
public void doSomething() {
em.lock(entity, LockModeType.PESSIMISTIC_WRITE); // entity is locked
// any other thread trying to update the entity until this method finishes will raise an error
}
...
object.doSomething();
object.doSomethingElse(); // lock is already released here
Have you tried to set the isolation level in your application server?
To get a lock on a row no matter what you are trying to do afterwards (read/write), you need to set the isolation level to TRANSACTION_SERIALIZABLE.
Lock fails only if another thread is already holding the lock. You can take two FOR UPDATE locks on single row in DB, so it's not JPA-specific thing.
I am using spring data/jpa to perform some database operations. I have a while loop which runs and successfully inserts data as it runs, but i also need an update operation to happen at the end of each run of a while loop. Here is basically what I have in a simple example. This is exactly the structure I am using.
Class doing all the operations:
#Component
public class MyClassImpl implements MyClass {
#Autowired
MyOtherClass myOtherClass;
#Override
public void run() {
while (expression) {
// get some data into and entity object
myOtherClass.insertMethod(entity);
myOtherClass.updateMethod(entityId);
}
}
}
my other class:
#Component
public class MyOtherClassImpl implements MyOtherClass {
#Override
JpaClass jpaClass;
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertMethod(EntityObject entity) {
jpaClass.save(entity);
}
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateMethod(String entityId) {
EntityObject entity = jpaClass.findById(entityId);
//change something on the entity
jpaClass.save(entity);
}
}
entity object:
public interface JpaClass extends JpaRepository<EntityObject, Long> {
EntityObject findById(String entityId);
}
the problem I am having is that the insert works just fine, but within the while loop I cannot get any updates to work like i have them. I have tried moving the logic around and putting the findById logic in a different method but cannot get it working. I am trying to update 1 row in a table which handles 1 value I then need to reference in the next run of the while loop.
so it goes:
get value
operate using value
update value
repeat
I set up the database config using spring #Configuration on a class which works fine for all transactions, for reference it is essentially set up like this:
#Configuration
#EnableTransactionManagement
#PropertySource(value = { "classpath:/${app.execution.environment}/application.properties" })
#EnableJpaRepositories(basePackages = "com.example", entityManagerFactoryRef = "mysqlEntityManager", transactionManagerRef = "mysqlTransactionManager")
public class MysqlHibernateConfig {
// all the needed beans here
}
Just to confirm as well, i ran this logic without the while loop and the data does update as expected, so the problem is somewhere in the database transaction, but I am stuck on how to resolve it.
This problem is caused by cache.
You can try like this.
in service class,
#Autowired
private EntityManager entityManager;
entityManager.clear();
in application.properties, you should set
spring.jpa.open-in-view = false
Consider the following situation:
#Stateless
#Clustered
public class FacadeBean implements Facade {
#EJB
private Facade facade;
#Override
public void foo(List<Integer> ids) {
// read specific id's from the db
for (Integer id : ids) {
facade.bar(id);
}
}
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void bar(Integer id) {
// save something to the db
}
}
Method foo gets called from outside the ejb. I want each id to be executed in its own transaction, so that data get's persisted directly to the database. It is not possible to have the foreach outside of this class. I am wondering what is the best way to do this? At the moment I am injecting the interface again, to step over the ejb boundaries (so that the TransactionAttribute get's evaluated).
Your approach as to circular reference is perfectly fine. Circular reference in EJBs is allowed. This is even mandatory in order to start out a new transaction, or an #Asynchronous thread (otherwise the current thread would still block).
#Stateless
public class SomeService {
#EJB
private SomeService self; // Self-reference is perfectly fine.
// Below example starts a new transaction for each item.
public void foo(Iterable<Long> ids) {
for (Long id : ids) {
self.fooInNewTransaction(id);
}
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void fooInNewTransaction(Long id) {
// ...
}
// Below example fires an async thread in new transaction.
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void bar(Iterable<Long> ids) {
for (Long id : ids) {
self.fooAsynchronously(id);
}
}
#Asynchronous
public void fooAsynchronously(Long id) {
// ...
}
}
Only in older containers, this did not work, most notably JBoss AS 5 with the ancient EJB 3.0 API. That's why people invented workarounds like SessionContext#getBusinessObject() or even manually grabbing via JNDI.
Those are unnecessary these days. Those are workarounds not solutions.
I'd personally only do it the other way round as to transactions. The foo() method is clearly never intented to be transactional.
#Stateless
public class SomeService {
#EJB
private SomeService self;
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void foo(Iterable<Long> ids) {
for (Long id : ids) {
self.foo(id);
}
}
public void foo(Long id) {
// ...
}
}
Depending on the concrete functional requirement, you could even make the foo(Long id) #Asynchronous, hereby speeding up the task.
Do you really have to have both methods in one class? You can move bar() to an own bean and make it transactional. Then you don't have to use this kind of self-injection.
You can also try to use SessionContext#getBusinessObject() method.
#Resource
SessionContext sessionContext;
#Override
public void foo(List<Integer> ids) {
Facade facade = sessionContext.getBusinessObject(Facade.class);
// read specific id's from the db
for (Integer id : ids) {
facade.bar(id);
}
}