Java: How to handle multiple Hibernate transactions in one request? - java

I'm not sure where to open my Transaction object. Inside the service layer? Or the controller layer?
My Controller basically has two services, let's call them AService and BService. Then my code goes something like:
public class Controller {
public AService aService = new AService();
public BService bService = new BService();
public void doSomething(SomeData data) {
//Transaction transaction = HibernateUtil.getSession().openTransaction();
if (data.getSomeCondition()) {
aService.save(data.getSomeVar1());
bService.save(data.getSomeVar2());
}
else {
bService.save(data.getSomeVar2());
}
//transaction.commit(); or optional try-catch with rollback
}
}
The behavior I want is that if bService#save fails, then I could invoke a transaction#rollback so that whatever was saved in aService would be rolled back as well. This only seems possible if I create one single transaction for both saves.
But looking at it in a different perspective, it looks really ugly that my Controller is dependent on the Transaction. It would be better if I create the Transaction inside the respective services, (something like how Spring #Transactional works), but if I do it that way, then I don't know how to achieve what I want to happen...
EDIT: Fixed code, added another condition. I am not using any Spring dependencies so the usage of #Transactional is out of the question.

You can accomplish what you're asking with another layer of abstraction and using composition.
public class CompositeABService {
#Autowired
private AService aservice;
#Autowired
private BService bservice;
#Transactional
public void save(Object value1, Object value2) {
aservice.save( value1 );
bservice.save( value2 );
}
}
public class AService {
#Transactional
public void save(Object value) {
// joins an existing transaction if one exists, creates a new one otherwise.
}
}
public class BService {
#Transactional
public void save(Object value) {
// joins an existing transaction if one exists, creates a new one otherwise.
}
}
This same pattern is typically used when you need to interact with multiple repositories as a part of a single unit of work (e.g. transaction).
Now all your controller needs to depend upon is CompositeABService or whatever you wish to name it.

Related

node response.end() equivalent in spring

Is there any way in spring that we can send response immediately.
I want to create a thread which will do a job. But I don't want to make the user to wait till that job completed.
There is multiple way of doing so in Spring.
Here is their article.
If you want to make the operations asynchronously, the easiest way is to use the #Asyn annotation from Spring.
Here is a simple example :
// Interface definition for your async operation here
public interface AsyncOperator {
#Async
void launchAsync(String aBody);
}
And a simple implementation that uses the interface
// Need to be a bean managed by Spring to be async
#Component
class SimpleAsync implements AsyncOperator {
#Override
public void launchAsync(String aBody){
// Your async operations here
}
}
Then you need for Spring to configure how the async works. Using Spring boot a simple configuration class like this works:
#Configuration
#EnableAsync
public class AsyncConfiguration {
}
Then you can call your method and it will return right away and do the treatments asynchronously :
#Component
public class AController {
private final AsyncOperator async;
public AController(AsyncOperator async){
this.async = async;
}
public String aMethod(String body){
// here it will return right after call
this.async.launchAsync(body);
return "Returned right away !!";
}
}
The only downsides of this method is that all your classes for async operations must be managed by Spring.

How to cache data during application startup in Spring boot application

I have a Spring boot Application connecting to SQL Server Database. I need some help in using caching in my application. I have a table for CodeCategory which has a list of codes for Many codes. This table will be loaded every month and data changes only once in a month.
I want to cache this entire table when the Application starts. In any subsequent calls to the table should get value from this cache instead of calling the Database.
For Example,
List<CodeCategory> findAll();
I want to cache the above DB query value during application startup. If there is a DB call like List<CodeCategory> findByCodeValue(String code) should fetch the code result from the already Cached data instead of calling the Database.
Please let me know how this can be achieved using spring boot and ehcache.
As pointed out, It takes some time for ehcache to setup and it is not working completely with #PostConstruct. In that case make use of ApplicationStartedEvent to load the cache.
GitHub Repo: spring-ehcache-demo
#Service
class CodeCategoryService{
#EventListener(classes = ApplicationStartedEvent.class )
public void listenToStart(ApplicationStartedEvent event) {
this.repo.findByCodeValue("100");
}
}
interface CodeCategoryRepository extends JpaRepository<CodeCategory, Long>{
#Cacheable(value = "codeValues")
List<CodeCategory> findByCodeValue(String code);
}
Note: There are multiple ways as pointed by others. You can choose as per your needs.
My way is to define a generic cache handler
#FunctionalInterface
public interface GenericCacheHandler {
List<CodeCategory> findAll();
}
And its implementation as below
#Component
#EnableScheduling // Important
public class GenericCacheHandlerImpl implements GenericCacheHandler {
#Autowired
private CodeRepository codeRepo;
private List<CodeCategory> codes = new ArrayList<>();
#PostConstruct
private void intializeBudgetState() {
List<CodeCategory> codeList = codeRepo.findAll();
// Any customization goes here
codes = codeList;
}
#Override
public List<CodeCategory> getCodes() {
return codes;
}
}
Call it in Service layer as below
#Service
public class CodeServiceImpl implements CodeService {
#Autowired
private GenericCacheHandler genericCacheHandler;
#Override
public CodeDTO anyMethod() {
return genericCacheHandler.getCodes();
}
}
Use the second level hibernate caching to cache all the required db queries.
For caching at the application start-up, we can use #PostContruct in any of the Service class.
Syntax will be :-
#Service
public class anyService{
#PostConstruct
public void init(){
//call any method
}
}
Use CommandLineRunner interface.
Basically , you can create a Spring #Component and implement CommandLineRunner interface. You will have to override it's run method. The run method will be called at the start of the app.
#Component
public class DatabaseLoader implements
CommandLineRunner {
#override
Public void run(.... string){
// Any code here gets called at the start of the app.
}}
This approach is mostly used to bootstrap the application with some initial data.

How to tell Hibernate NOT to store data while running JUnit tests?

I want to check my persistence logic and so I am running some test cases.
Repository class:
#Repository
public class MyRepository {
public void add(Object obj) {
/* Do some stuff here */
getSession().persist(obj);
}
}
Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { Context.class})
public class MyTests {
#Inject
private MyRepository myRepository;
#Test
#Rollback(true)
public void foo() {
/* Test logic */
myRepository.add(obj);
Assert.assert...;
}
}
The unit test: MyTests.java contains test cases which test some stuff that is related to persistence, but not the actual Hibernate persistence itself, so that's why the getSession.persist() statement is obsolete.
For performance reasons, I want to prevent Hibernate from storing data to my database, even if the whole data interaction is rolled back. My approach would be to mock: getSession().persist(). Is there a better, or more specifically, an easier way to achieve my intentions?
First of all, there are different id generators in Hibernate. If identity generator (not all the databases supports it), then to assign id to the entity, when session.persist method is called, insert query will be called. But if, for example, sequence or uuid generator is used, then insert won't be triggered (at least right away).
After that if methods session.get or session.load are called to load persisted (in the current session) object, then select query won't be called, because it gets object from Hibernate cache. But if HQL is used to select data, then select query is called. Moreover before it (by default) insert query for persisted object is called too.
This can be changed with FlushMode. By default is set to AUTO. It means:
The Session is sometimes flushed before query execution in order to
ensure that queries never return stale state.
But if getSession().setHibernateFlushMode(FlushMode.MANUAL) is set:
The Session is only ever flushed when Session.flush() is explicitly
called by the application.
Which means insert query won't be called until session.flush is called explicitly. If methods session.get and session.load are further used your code will still work (in the current session). But in case of select HQL query - it won't find the entity since it wasn't persisted. So beware.
Create an interface, implement it using the Hibernate persist() method, and use it in such a way, that:
the normal calls go through the implementation
the test calls go through a mock version of it
public interface MyRepository {
public void add(Object obj);
}
public class MyRepositoryImpl implements MyRepository {
public void add(Object obj) {
getSession().persist(obj);
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { Context.class})
public class MyTests {
#Mock // we inject the mock instead of the true implementation
private MyRepository myRepository;
#Test
#Rollback(true)
public void foo() {
/* Test logic */
myRepository.add(obj); // the test uses the mocked version
Assert.assert...;
}
}
There are many Java libraries that let you mock objects, e.g.
Mockito
JMock
EasyMock
You need to be able to mock your repository object so that you can use the mock in tests, and use the real one in the rest of your application.
DAO:
#Repository(value="MockRepo")
public class MockMyRepositoryImpl implments MyRepository {
#Override
public void add(Foo foo) {
//Do nothing here
}
}
Test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { Context.class})
public class MyTests {
#Autowired
#Qualifier("MockRepo");
private MyRepository repo;
#Test
public void testFooSave() {
repo.add(obj);
}
}
The alternative is to use a mocking framework as detailed in another answer. Mocking frameworks are more flexible, but if you want something simple that's just going to work then try the above.

DDD: Delete on a Repository vs delete on an Entity?

A very simple use case implemented using DDD and java.
I have a FooEntity and a FooRepository. The Entity has a delete method which validates certain state to check whether it is safe to be deleted, and in case this evaluates to true invoke the delete in the repository, which is injected in the entity.
So far so good, but, what happens if somebody invokes the delete method directly in the repository? Then the validation wouldn't be performed.
Placing the validation in the repository would solve the problem, but this would be clearly wrong since it would make necessary to expose the internal state of the entity.
What am I missing?
public class FooEntity {
#inject
FooRepository fooRepository;
private Boolean canBeDeleted;
public void delete(){
if (canBeDeleted){
fooRepository.delete(this);
}
throw new CannotBeDeletedException();
}
}
public class FooRepository {
#inject
FooDAO fooDAO;
public void delete(FooEntity fooEntity){
fooDAO.delete(fooEntity.getId());
}
}
Don't expose the internal state, expose a method like isDeletable() on the entity. The repository's delete can call entity.isDeletable() before deleting, and raise an exception if you are trying to delete an entity that is not deletable. That way you separate the concerns. The entity has the domain knowledge of it's "deletableness", while the repo knows how to delete the entity.
The example code is fine as is (except that it's strange to have a DAO inside a repository class, as "repository" is just a more abstract name for the same concept as the DAO).
You can't really prevent other developers from calling the wrong methods, except for using static analysis code inspections where available.
The repository should only concern itself with removing the given entity instance from the set of persistent entities. It cannot have logic for checking whether the entity is allowed to be deleted or not, even if the isDeletable() method is in the entity class.
I would put the delete functionality in a domain service.
public class FooService {
#inject
FooRepository fooRepository;
public void delete(Foo foo) {
if( /* insert validation stuff here to check if foo can be deleted */ ) {
fooRepository.delete(foo);
}
}
The way I do it though is I typically use a ValueObject to represent an Entity's identity. E.g.
public class FooId() {
String foodId;
public String FooId(String fooId) {
this.foodId = foodId;
}
}
public class Foo() {
FooId id;
/* other properties */
}
I would then revise FooService to:
public class FooService {
#inject
FooRepository fooRepository;
public void delete(FooId fooId) {
foo = fooRepository.retrieve(fooId);
if( /* insert validation stuff here to check if foo can be deleted */ ) {
fooRepository.delete(foo);
}
}
To delete a foo (assuming fooId was passed by a command from the UI:
fooService.delete(fooId);
I would not inject a FooRepository in an a class that represents entity. I don't think that it is the rightful place. An Entity for me should not be able to create or delete itself. These functions should be in a Domain Service for that Entity.

Java Spring #Transactional method not rolling back as expected

Below is a quick outline of what I'm trying to do. I want to push a record to two different tables in the database from one method call. If anything fails, I want everything to roll back. So if insertIntoB fails, I want anything that would be committed in insertIntoA to be rolled back.
public class Service {
MyDAO dao;
public void insertRecords(List<Record> records){
for (Record record : records){
insertIntoAAndB(record);
}
}
#Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void insertIntoAAndB(Record record){
insertIntoA(record);
insertIntoB(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoA(Record record){
dao.insertIntoA(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoB(Record record){
dao.insertIntoB(record);
}
public void setMyDAO(final MyDAO dao) {
this.dao = dao;
}
}
Where MyDAO dao is an interface that is mapped to the database using mybatis and is set using Spring injections.
Right now if insertIntoB fails, everything from insertIntoA still gets pushed to the database. How can I correct this behavior?
EDIT:
I modified the class to give a more accurate description of what I'm trying to achieve. If I run insertIntoAAndB directly, the roll back works if there are any issues, but if I call insertIntoAAndB from insertRecords, the roll back doesn't work if any issues arise.
I found the solution!
Apparently Spring can't intercept internal method calls to transactional methods. So I took out the method calling the transactional method, and put it into a separate class, and the rollback works just fine. Below is a rough example of the fix.
public class Foo {
public void insertRecords(List<Record> records){
Service myService = new Service();
for (Record record : records){
myService.insertIntoAAndB(record);
}
}
}
public class Service {
MyDAO dao;
#Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void insertIntoAAndB(Record record){
insertIntoA(record);
insertIntoB(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoA(Record record){
dao.insertIntoA(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoB(Record record){
dao.insertIntoB(record);
}
public void setMyDAO(final MyDAO dao) {
this.dao = dao;
}
}
I think the behavior you encounter is dependent on what ORM / persistence provider and database you're using. I tested your case using hibernate & mysql and all my transactions rolled back alright.
If you do use hibernate enable SQL and transaction logging to see what it's doing:
log4j.logger.org.hibernate.SQL=DEBUG
log4j.logger.org.hibernate.transaction=DEBUG
// for hibernate 4.2.2
// log4j.logger.org.hibernate.engine.transaction=DEBUG
If you're on plain jdbc (using spring JdbcTemplate), you can also debug SQL & transaction on Spring level
log4j.logger.org.springframework.jdbc.core=DEBUG
log4j.logger.org.springframework.transaction=DEBUG
Double check your autocommit settings and database specific peciular (eg: most DDL will be comitted right away, you won't be able to roll it back although spring/hibernate did so)
Just because jdk parses aop annotation not only with the method, also parse annotation with the target class.
For example, you have method A with #transactional, and method B which calls method A but without #transactional, When you invoke the method B with reflection, Spring AOP will check the B method with the target class has any annotations.
So if your calling method in this class is not with the #transactional, it will not parse any other method in this method.
At last, show you the source code:
org.springframework.aop.framework.jdkDynamicAopProxy.class
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
......
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping orfancy proxying.
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
}

Categories