I'm using a UnitOfWork in a background task method (operated by Quartz) with Guice-persist on top of hibernate. The background task call a service, which need to commit the current transaction in the middle of it's task - and continue on another transaction. How can I commit the current UnitOfWork and create a new one?
class BackgroundJob {
#Inject UnitOfWork unitOfWork;
#Inject MyService myService;
public void run() {
try {
unitOfWork.begin();
myService.foo();
} finally {
unitOfWork.end();
} } }
class MyServiceImpl implements MyService {
#Override public void foo() {
foo1();
// I would like to commit here and start a new transaction
foo2();
} }
The service is also managed by Guice, but is a singleton, and do not have access to the caller UnitOfWork as is.
Ideally I do not want to change service signature. A workaround is for the caller to give two UnitOfWork as parameters to foo(), but this seems a bit hacked.
EDIT: For ease of use of future fellow reader, here is my implementation of the solution given by ColinD, which fits the bill nicely:
class BackgroundJob {
#Inject UnitOfWork unitOfWork;
#Inject MyService myService;
public void run() {
try {
unitOfWork.begin();
myService.foo();
} finally {
unitOfWork.end();
} } }
class MyServiceImpl implements MyService {
#Override public void foo() {
foo1();
foo2();
}
#Transactional private void foo1() { ... }
#Transactional private void foo2() { ... }
}
If I recall correctly, a unit of work in Guice Persist is not a single transaction. Rather, it represents a single unit of work such as a single web request or, in your case, a single background job. I believe that a single database connection is used for a whole unit of work, but that unit of work may have multiple distinct transactions. In fact, I think that just starting and ending a unit of work will not cause any transactions to be started or ended.
I think what you want to do is to annotate both foo1() and foo2() (but not foo()) with #Transactional. As long as there's no outer transaction wrapping both calls, they'll each run in a separate transaction.
This may fit the bill.
class BackgroundJob {
#Inject UnitOfWork unitOfWork;
#Inject MyService myService;
public void run() {
try {
unitOfWork.begin();
myService.foo1();
unitOfWork.end();
unitOfWork.begin();
myService.foo2();
unitOfWork.end();
} finally {
unitOfWork.end();
}
}
}
Related
I have an issue with rollbacking in #Transactional. When rollback is called, rollback itself is working, but another method after is executed.
#Transactional(rollbackFor = { Exception.class })
#Service
public class Service {
public void method1() {
stuffToGetListOfObjects();
deleteAllAndSaveAll(listOfObejcts);
Util.staticMethod();
}
private deleteAllAndSaveAll(List list) {
repository.deleteAll();
repository.saveAll(list);
}
}
#SpringBootApplication
public class Application implements ApplicationRunner {
private final Service service;
#Autowired
public Application(Service service) {
this.service = service;
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
service.method1();
}
}
When something goes wrong during the insertion in repository.saveAll(list), no data is actually deleted which is fine and expected. The issue is program is going on the Util.staticMethod() method is executed "after" rollback.
I know that static methods cannot be #Transactional and private methods are ignored, but that doesn't seem the issue here. According to the log, everything in method1() is executed first and after that, the inserting is happening. I guess I need to pick out the Util.staticMethod() calling from transaction somehow.
That's why you should separate the repository actions from the service functions. Repository actions are rollbacked. If you separate them the repository level will throw an error and rollback itself. And before the static method, you can check whether the transaction is finished or not by #transactionaleventlistener.
I have a method something like:
#Transactional
public void method1() {
taskExecutor.execute() -> {
// do work here
}
}
The #Transactional annotation isn't honored due to new thread pool.
How can I fix this?
You can try to write a Class with a method of your work(will do) and set the #Transactional annotation on this method.Then inject this Class and invoke its work method in new thread pool.
For example:
public class WorkClass {
#Transactional
public void work() {
//do work here...
}
}
#autowired
private WorkClass workClass;
#Transactional
public void method1() {
taskExecutor.execute() -> {
workClass.work();
}
}
And you can adjust policy by Spring Transaction Propagation.
I wonder, what happens when #Transactional(readonly=true) method calls #Transactional(readonly=false) method? Talking about the situation with propagation = Propagation.REQUIRED (within the outer transaction scope).
public class ServiceA {
ServiceB serviceB;
#Transactional(readonly = true)
public void readOnly() {
// some reading from repo
serviceB.write()
}
}
public class ServiceB {
#Transactional
public void write() {
// some write to repo
}
}
The same question for the opposite situation, what happens if #Transactional(readonly=false) method calls #Transactional(readonly=true) method? I suppose that for the last case it would just consider #Transactional(readonly=false) from the outer scope.
Calling readOnly=false from readOnly=true doesn't work since the previous transaction continues.
This is because the previous transactional is being continued.
If you want this to work, you must have it start a new transaction. Sample:
public class ServiceB {
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void write() {
// some write to repo ..
}
}
In this case, it will work because a new transactional will be started.
I have a service implementation with a particular method like so:
public class ExampleServiceImpl implements ExampleService {
#AutoWired
#Resource
private RecordRepository recordRepository;
private void processRecord() {
// some code here
}
#Transactional(readOnly=false)
public void processRecord(Record a) {
Record original = getOriginal(a);
recordRepository.saveChanges(a,original);
}
}
Where the Record class is the root object of an object graph. RecordRepository looks something like the following with sub repositories to save various children of the objects in the graph.
public class RecordRepository extends BaseRepository<Record> {
#AutoWired
#Resource
private IDao databaseDao;
#AutoWired
#Resource
private SubRecordRepository subRecordRepository;
public void saveChanges(Record a, Record b) {
//Perform some processing on a, b
for(SubRecord subA : a.getSubRecords()) {
subRecordRepository.saveChanges(subA);
}
databaseDao.updateRecord(a);
}
}
public class DatabaseDao extends NamedParameterJdbcDaoSupport implements IDao {
#Autowired
public DatabaseDao(#Qualifier("org.somewhere.Datasource") DataSource ds) {
super();
this.setDataSource(ds);
}
public void updateRecord(Record inRecord) {
String query = (String) sql.get("updateRecord");
SqlParameterSource parms = new BeanPropertySqlParameterSource(inRecord);
getNamedParameterJdbcTemplate().update(query, parms);
}
public void insertSubRecord(SubRecord inSubRecord) {
String query = (String) sql.get("insertSubRecord");
SqlParameterSource parms = new BeanPropertySqlParameterSource(inSubRecord);
getNamedParameterJdbcTemplate().insert(query, parms);
}
// other update and insert methods
}
Will the transaction be applied across all involved inserts\updates from the processRecord call? In other words, if an insert or update fails, will all previously called inserts and updates from ExampleServiceImpl.processRecord get rolled back?
Yes. The transactional aspect makes sure that a transaction is started before the annotated method is called, and that the transaction (if started by this method) is committed or rollbacked once the method returns.
The transactional interceptor doesn't know (and doesn't care) about which other methods are called inside the annotated method. Every read and write to the DataSource handled by the Spring transaction manager will be included in the same transaction.
I am having a bean within which I create a new Thread with Runnable:
#Component
public class MyBean {
private final Task task = new Task();
#PersistenceContext
EntityManager em;
#PostConstruct
public void init() {
task.setEntityManager(em);
new Thread(task).start();
}
public static class Task implements Runnable {
#Setter
private EntityManager em;
#Override
public void run() {
while (true) {
// working with EntityManager
Thing t = em.findById(...); // Fetching a Thing from repo
t.getSomethingList(); // LazyInit exception
wait();
}
}
}
}
Withing the init method, new Thread is created with instance of EntityManager. When I try to load something from the repository the session is instantly closed and getting any lazy field results in failed to lazily initialize a collection of role: Something, no session or session was closed exception from Hibernate.
I tried all the #Transactional annotations with no effect. I need to achieve something like OpenEntityManagerInView, but with the difference that this is not within view.
Thanks
EDIT1:
According to comments - I tried using em.getTransaction().begin(); but this is getting me Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT.
skirsch suggested that I should invoke Transactional method on some other bean. That is what I actually do - exactly as you suggested. I wanted to make things simpler and I did not realize the difference, so I demostrated the problem directly in the class Task. So to summarize, I have it exactly like skirsch suggested, but the problem persists.
As Spring is not managing your Runnable, annotating it won't have the desired effect. So you either need to use an annotated (and Spring-managed) bean from within your Runnable or you need to take care of the txn management manually.
Use Spring transaction management
You define some kind of service
#Service
public class MyService {
#PersistenceContext
EntityManager em;
#Transactional
public void doSomething() {
Thing t = em.findById(...);
t.getSomethingList();
}
}
And then your bean would look like this:
#Component
public class MyBean {
private final Task task = new Task();
#Autowired
MyService service;
#PostConstruct
public void init() {
task.setService(service);
new Thread(task).start();
}
public static class Task implements Runnable {
#Setter
private MyService service;
#Override
public void run() {
while (true) {
service.doSomething();
wait();
}
}
}
}
Manual transaction management
In case you set up JPA Resource Local Transactions, here you go:
// working with EntityManager
em.getTransaction().begin()
try {
Thing t = em.findById(...);
t.getSomethingList();
} finally {
em.getTransaction().rollback()
}
wait();