Using transactional in spring boot with hibernate - spring-boot

I am getting the error while using hibernate in spring boot application No qualifying bean of type TransactionManager' available
I am using the following config class:
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#org.springframework.context.annotation.Configuration
#EnableTransactionManagement
public class Config {
#Bean
public SessionFactory sessionFactory() {
Configuration configuration = new Configuration();
configuration.configure();
configuration.addAnnotatedClass(Ct.class);
configuration.addAnnotatedClass(St.class);
SessionFactory sessionFactory = configuration.buildSessionFactory();
return sessionFactory;
}
}
#RestController
public class RestAPIController {
#Autowired
private SessionFactory sessionFactory;
#PutMapping("/addS")
#Transactional
public void addSt(#RequestParam("cc") String cc,#RequestParam("st") String st) {
CC cc1= new CC();
CC.setCode(cc);
State state = new State(cc,st);
sessionFactory.getCurrentSession().save(state);
}
}
}
The main reason I added the #Transactional in the addSt method is due to error: The transaction was still an active when an exception occurred during Database.
So I turned to use spring boot for managing transactions. I am not sure what to do here.
--------------------UPDATED CODE--------------------
#Repository
public interface StateRepository extends CrudRepository<State, String> {}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.ArrayList;
import java.util.List;
#Service
#Transactional
public class StateService {
#Autowired
private StateRepository stateRepository;
public void save(State state) {
stateRepository.save(state);
}
public List<State> findAll() {
List<State> states = new ArrayList<>();
stateRepository.findAll().forEach(states::add);
return states;
}
}

For starters use proper layers and write a service and use JPA instead of plain Hibernate. If you want a Session you can always use EntityManager.unwrap to obtain the underlying Session.
#Service
#Transactional
public StateService {
#PersistenceContext
private EntityManager em;
public void save(State state) {
em.persist(state);
}
Use this service in your controller instead of the SessionFactory.
#RestController
public class RestAPIController {
private final StateService stateService;
RestAPIController(StateService stateService) {
this.stateService=stateService;
}
#PutMapping("/addS")
public void addSt(#RequestParam("cc") String cc, #RequestParam("st") String st) {
CC cc1= new CC();
CC.setCode(cc);
State state = new State(cc,st);
stateService.save(state);
}
}
Now ditch your Config class and restart the application.
NOTE
When using Spring Data JPA it is even easier, define a repository extending CrudRepository and inject that into the service instead of an EntityManager. (I'm assuming that Long is the type of primary key you defined).
public interface StateRepository extends CrudRepository<State, Long> {}
#Service
#Transactional
public StateService {
private final StateRepository states;
public StateService(StateRepository states) {
this.states=states;
}
public void save(State state) {
states.save(state);
}
}

Related

No qualifying bean of type 'testgroup.private_clinic.dao.PatientDAO' available in SpringBoot Application

I'm developing crud application, and experiencing difficulties with springboot, which fails on startup. This is what i got:
20764 WARNING [main] --- org.springframework.context.annotation.AnnotationConfigApplicationContext: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'patientServiceImpl': Unsatisfied dependency expressed through method 'setPatientDAO' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'testgroup.private_clinic.dao.PatientDAO' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Screenshot of project structure:
Model:
package testgroup.private_clinic.model;
import javax.persistence.*;
#Entity
#Table(name="Patients")
public class Patient {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
int id;
#Column(name="patient_name")
String name;
#Column(name = "patient_surname")
String surname;
#Column(name = "patient_patronimic")
String patronimic;
#Column(name="adress")
String adress;
#Column(name = "status")
String status;
#Column(name="diagnosis")
String diagnosis;
//+getters and setters
Controller:
package testgroup.private_clinic.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import testgroup.private_clinic.model.Patient;
import testgroup.private_clinic.service.PatientService;
import java.util.List;
#RestController
public class PatientController {
PatientService patientService;
#Autowired
public void setPatientService(PatientService patientService){
this.patientService = patientService;
}
#RequestMapping(method = RequestMethod.GET)
public ModelAndView allPatients(){
List<Patient> patients = patientService.allPatients();
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("patients");
modelAndView.addObject("patientList", patients);
return modelAndView;
}
#RequestMapping(value= "/edit{id}", method = RequestMethod.GET)
public ModelAndView editPage(#PathVariable("id") int id){
Patient patient = patientService.getByID(id);
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("editPage");
modelAndView.addObject("patient", patient);
return modelAndView;
}
#RequestMapping(value="/edit", method = RequestMethod.POST)
public ModelAndView editPatient(#ModelAttribute("patient") Patient patient){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("redirect:/");
patientService.edit(patient);
return modelAndView;
}
}
Repository:
package testgroup.private_clinic.dao;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import testgroup.private_clinic.model.Patient;
import javax.transaction.Transactional;
import java.util.*;
#Repository
public class PatientDAOImpl implements PatientDAO {
SessionFactory sessionFactory;
#Autowired
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
#Override
#Transactional
public List<Patient> allPatients() {
Session session = sessionFactory.getCurrentSession();
return session.createQuery("from Patient").list();
}
#Override
#Transactional
public void add(Patient patient) {
Session session = sessionFactory.getCurrentSession();
session.persist(patient);
}
#Override
#Transactional
public void delete(Patient patient) {
Session session = sessionFactory.getCurrentSession();
session.delete(patient);
}
#Override
#Transactional
public void edit(Patient patient) {
Session session = sessionFactory.getCurrentSession();
session.update(patient);
}
#Override
#Transactional
public Patient getByID(int id) {
Session session = sessionFactory.getCurrentSession();
return session.get(Patient.class, id);
}
}
Service:
package testgroup.private_clinic.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import testgroup.private_clinic.model.Patient;
import testgroup.private_clinic.dao.PatientDAO;
import javax.transaction.Transactional;
import java.util.List;
#Service
public class PatientServiceImpl implements PatientService{
PatientDAO patientDAO;
#Autowired
public void setPatientDAO(PatientDAO patientDAO){
this.patientDAO = patientDAO;
}
#Transactional
#Override
public List<Patient> allPatients() {
return patientDAO.allPatients();
}
#Transactional
#Override
public void add(Patient patient) {
patientDAO.add(patient);
}
#Transactional
#Override
public void delete(Patient patient) {
patientDAO.delete(patient);
}
#Transactional
#Override
public void edit(Patient patient) {
patientDAO.edit(patient);
}
#Transactional
#Override
public Patient getByID(int id) {
return patientDAO.getByID(id);
}
}
Main class:
package testgroup.private_clinic.service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SpringBootClass {
public static void main(String[] args){
SpringApplication.run(testgroup.private_clinic.service.SpringBootClass.class, args);
}
}
Spring Boot uses classpath component scanning meaning, your entry-point class which is SpringBootClass will scan for all beans within its class path unless you configure it not to.
Looking into your project structure, SpringBootClass is under the testgroup.private_clinic.service package thus Spring Boot will only scan this package for beans and it only found the PatientServiceImpl however, before it injects this to the application context, it will need to inject first its dependency PatientDAO which is not part of the testgroup.private_clinic.service package thus, explains your error.
You have two options to fix this:
Move your SpringBootClass to the base package testgroup.private_clinic - this will make Spring Boot scan on all you components under this package and its sub-packages (e.g., service, dao)
Use #ComponentScan on your SpringBootClass and define there the base package you want to scan for beans.
#ComponentScan(basePackages = "testgroup.private_clinic")
Thanks and cheers!

#Autowired is Null in Spring Boot but same is accessible through Application Context

Through #Autowired i am not able to access the #Component/#Service/#Respository/#Controller class objects in other java files which has #Component annotation (Step 1: Approach) with the Step 1 approach getting Null pointer Exception, but same i could achieve using (Step 2: Approach).
Can anyone please tell me why i am not able to achieve using Step 1 approach:
FYI- I've searched in my entire project i have not used/called/initialized the #Component classes using new method for the autowired class still i getting the issue as "Null Pointer Exception"
Step 1: Using #Autowired Annotation
#Component
public class Processor {
#Autowired
PropertyConfigurator propconfigrator; --> Getting here as null pointer Exception
public void getDetails(){
System.out.println ("Application URL +propconfigrator.getProperties().getProperty("appURL"));
}
}
Step 2: Using ApplicationContext Interface with/without #AutoWired annotation . I am able to get the property value from PropertyConfigurator java file
#Component
public class Processor {
#Autowired
PropertyConfigurator propconfigrator = ApplicationContextHolder.getContext().getBean(PropertyConfigurator.class);
public void getDetails(){
System.out.println ("Application URL +propconfigrator.getProperties().getProperty("appURL"));
}
}
ApplicationContextHolder.java
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getContext() {
return context;
}
}
PropertyConfigurator.java file
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
#Service
#Configurable
public class PropertyConfigurator {
private final Properties properties;
public Properties getProperties () {
return properties;
}
public PropertyConfigurator(){
properties = new Properties();
try {
properties.load(getClass().getClassLoader().getResourceAsStream("dbconfig.properties"));
} catch (IOException e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, e.getMessage(), e);
}
}
}
Why did you use #Configurable annotation? In the code you postet, it doesn't make sense. #Configurable is only needed in cases when instances of this class are not createt by spring.
I have changed into Constructor Injection Autowiring as below with the step 1 approach of above (Not using Step 2. It resolved my issue finally.
Not sure why Spring is not able to inject the bean without using the Constructor Autowiring.
Step 1: Using #Autowired Annotation with Constructor
#Component
public class Processor {
#Autowired
public Processor (PropertyConfigurator propconfigrator) {
this.propconfigrator = propconfigrator;
}
public void getDetails(){
System.out.println ("Application URL +propconfigrator.getProperties().getProperty("appURL"));
}
}

Spring repository mvc how autowiring through interfaces and reaching specific repository implementations from a controller?

Good Morning,
I am building a web application and I chose to do it with an annotation driven spring mvc with REST Webservices (Jackson).
I am not using spring-boot because I wanted to add the libraries gradually when I needed them.
When I try to reach my specific repository with String str = ((GroupeMaterielRepository) repository).test(); i get a
java.lang.ClassCastException: com.sun.proxy.$Proxy210 cannot be cast
to pro.logikal.gestsoft.repository.GroupeMaterielRepository]
I would like to know how to access to my specific repository methods in which my HQL requests would be stored. I am trying to find a solution for days without success. The best I could do so far was accessing my CRUD methods in the generic repository implementation, but this implies to store in my repository interface every HQL method in the app, which will result as ugly.
I would like you to help me to get this code to work, keeping the logic of autowiring through interface's implementations extended by a more specific class with a controller layer and a repository layer.
Generic Controller :
package pro.logikal.gestsoft.controller;
import pro.logikal.gestsoft.repository.GenericCRUD;
public class GenericRestController<T> {
protected GenericCRUD<T> repository;
public GenericRestController(GenericCRUD<T> repository) {
this.repository = repository;
}
public GenericCRUD<T> getRepository() {
return repository;
}
}
Specific Controller :
package pro.logikal.gestsoft.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import pro.logikal.gestsoft.entity.GroupeMateriel;
import pro.logikal.gestsoft.repository.GenericCRUD;
import pro.logikal.gestsoft.repository.GroupeMaterielRepository;
#RestController
public class MaterielRESTController extends GenericRestController<GroupeMateriel> {
#Autowired
public MaterielRESTController(GenericCRUD<GroupeMateriel> repository) {
super(repository);
// TODO Auto-generated constructor stub
}
#GetMapping("/mat/groupes")
public ResponseEntity<String> getGroupes(){
String str = ((GroupeMaterielRepository) repository).test();
return new ResponseEntity<String>(str, HttpStatus.OK);
}
}
Repository Interface :
package pro.logikal.gestsoft.repository;
import java.util.List;
public interface GenericCRUD<T> {
void create(T entity);
void update(T entity);
void refresh(T entity);
void delete(Integer id);
T find (Integer id);
List<T> list();
}
Repository implementation :
package pro.logikal.gestsoft.repository;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.annotation.Transactional;
import pro.logikal.gestsoft.statics.ClientRequestUtils;
import pro.logikal.gestsoft.statics.DatabaseUtils;
#SuppressWarnings("unchecked")
#Transactional(DatabaseUtils.TM_GESTSOFT)
public class GenericCRUDImpl<T> implements GenericCRUD<T> {
#SuppressWarnings("unused")
public final Class<T> persistentClass;
#Autowired
#Qualifier(DatabaseUtils.GESTSOFT_SESSION)
public SessionFactory sessionFactory;
protected Session getCurrentSession() {
Session session = sessionFactory.getCurrentSession();
return session;
}
public GenericCRUDImpl(){
this.persistentClass= (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
#Override
public void create(final T entity) {
this.getCurrentSession().save(entity);
}
#Override
public void update(final T entity) {
this.getCurrentSession().update(entity);
}
#Override
public void refresh(final T entity) {
this.getCurrentSession().refresh(entity);
}
#Override
public void delete(Integer id) {
this.getCurrentSession().delete(this.find(id));
}
#Override
public T find(Integer id) {
// TODO Auto-generated method stub
return this.getCurrentSession().get(persistentClass, id);
}
#Override
public List<T> list() {
return this.getCurrentSession().createQuery("from "+persistentClass.getTypeName()).getResultList();
}
}
Repository associated to an entity and which is meant to contain the HQL requests for the related entities :
package pro.logikal.gestsoft.repository;
import org.springframework.stereotype.Repository;
import pro.logikal.gestsoft.entity.GroupeMateriel;
#Repository
public class GroupeMaterielRepository extends GenericCRUDImpl<GroupeMateriel> {
public String test() {
return "ok";
}
}
Just found out from where my problem came from reading [https://stackoverflow.com/a/6512431/8822802][1]
in my case #EnableAspectJAutoProxy(proxyTargetClass = true) on my config file

Applying custom annotation advice to spring data jpa repository

I am working on a mysql master slave replication. I am using spring data jpa(spring boot).
What I needed is all write operations to go to master server and read-only operations to be equally distributed among multiple read-only slaves.
For that I need to:
Use special JDBC driver: com.mysql.jdbc.ReplicationDriver
Set replication: in the URL:
spring:
datasource:
driverClassName: com.mysql.jdbc.ReplicationDriver
url: jdbc:mysql:replication://127.0.0.1:3306,127.0.0.1:3307/MyForum?user=root&password=password&autoReconnect=true
test-on-borrow: true
validation-query: SELECT 1
database: MYSQL
Auto commit needs to be turned off. (*)
Connection needs to be set to read-only.
To ensure JDBC Connection is set to read-only, I created an annotation and a simple AOP interceptor.
Annotation
package com.xyz.forum.replication;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by Bhupati Patel on 02/11/15.
*/
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface ReadOnlyConnection {
}
Interceptor
package com.xyz.forum.replication;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.hibernate.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.persistence.EntityManager;
/**
* Created by Bhupati Patel on 02/11/15.
*/
#Aspect
#Component
public class ConnectionInterceptor {
private Logger logger;
public ConnectionInterceptor() {
logger = LoggerFactory.getLogger(getClass());
logger.info("ConnectionInterceptor Started");
}
#Autowired
private EntityManager entityManager;
#Pointcut("#annotation(com.xyz.forum.replication.ReadOnlyConnection)")
public void inReadOnlyConnection(){}
#Around("inReadOnlyConnection()")
public Object proceed(ProceedingJoinPoint pjp) throws Throwable {
Session session = entityManager.unwrap(Session.class);
ConnectionReadOnly readOnlyWork = new ConnectionReadOnly();
try{
session.doWork(readOnlyWork);
return pjp.proceed();
} finally {
readOnlyWork.switchBack();
}
}
}
Following is my spring data repository
package com.xyz.forum.repositories;
import com.xyz.forum.entity.Topic;
import org.springframework.data.repository.Repository;
import java.util.List;
/**
* Created by Bhupati Patel on 16/04/15.
*/
public interface TopicRepository extends Repository<Topic,Integer>{
Topic save(Topic topic);
Topic findByTopicIdAndIsDeletedFalse(Integer topicId);
List<Topic> findByIsDeletedOrderByTopicOrderAsc(Boolean isDelete);
}
Following is my Manager(Service) class.
package com.xyz.forum.manager;
import com.xyz.forum.domain.entry.impl.TopicEntry;
import com.xyz.forum.domain.exception.impl.AuthException;
import com.xyz.forum.domain.exception.impl.NotFoundException;
import com.xyz.forum.entity.Topic;
import com.xyz.forum.replication.ReadOnlyConnection;
import com.xyz.forum.repositories.TopicRepository;
import com.xyz.forum.utils.converter.TopicConverter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
/**
* Created by Bhupati Patel on 16/04/15.
*/
#Repository
public class TopicManager {
#Autowired
TopicRepository topicRepository;
#Transactional
public TopicEntry save(TopicEntry topicEntry) {
Topic topic = TopicConverter.fromEntryToEntity(topicEntry);
return TopicConverter.fromEntityToEntry(topicRepository.save(topic));
}
#ReadOnlyConnection
public TopicEntry get(Integer id) {
Topic topicFromDb = topicRepository.findByTopicIdAndIsDeletedFalse(id);
if(topicFromDb == null) {
throw new NotFoundException("Invalid Id", "Topic Id [" + id + "] doesn't exist ");
}
return TopicConverter.fromEntityToEntry(topicFromDb);
}
}
In the above code #ReadOnlyConnection annotation is specified in manager or service layer. Above pieces of code works fine for me. It is a trivial case where in the service layer I am only reading from slave db and writing into master db.
Having said that my actual requirement is I should be able to use #ReadOnlyConnection in repository level itself because I have quite a few business logic where I do both read/write operation in other classes of service layer.Therefore I can't put #ReadOnlyConnection in service layer.
I should be able to use something like this
public interface TopicRepository extends Repository<Topic,Integer>{
Topic save(Topic topic);
#ReadOnlyConnection
Topic findByTopicIdAndIsDeletedFalse(Integer topicId);
#ReadOnlyConnection
List<Topic> findByIsDeletedOrderByTopicOrderAsc(Boolean isDelete);
}
Like spring's #Transactional or #Modifying or #Query annotation. Following is an example of what I am referring.
public interface AnswerRepository extends Repository<Answer,Integer> {
#Transactional
Answer save(Answer answer);
#Transactional
#Modifying
#Query("update Answer ans set ans.isDeleted = 1, ans.deletedBy = :deletedBy, ans.deletedOn = :deletedOn " +
"where ans.questionId = :questionId and ans.isDeleted = 0")
void softDeleteBulkAnswers(#Param("deletedBy") String deletedBy, #Param("deletedOn") Date deletedOn,
#Param("questionId") Integer questionId);
}
I am novice to aspectj and aop world, I tried quite a few pointcut regex in the ConnectionInterceptor but none of them worked. I have been trying this since a long time but no luck yet.
How to achieve the asked task.
I couldn't get a workaround of having my custom annotation #ReadOnlyConnection(like #Transactional) at a method level,but a small heck did work for me.
I am pasting the code snippet below.
#Aspect
#Component
#EnableAspectJAutoProxy
public class ConnectionInterceptor {
private Logger logger;
private static final String JPA_PREFIX = "findBy";
private static final String CUSTOM_PREFIX = "read";
public ConnectionInterceptor() {
logger = LoggerFactory.getLogger(getClass());
logger.info("ConnectionInterceptor Started");
}
#Autowired
private EntityManager entityManager;
#Pointcut("this(org.springframework.data.repository.Repository)")
public void inRepositoryLayer() {}
#Around("inRepositoryLayer()")
public Object proceed(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().getName();
if (StringUtils.startsWith(methodName, JPA_PREFIX) || StringUtils.startsWith(methodName, CUSTOM_PREFIX)) {
System.out.println("I'm there!" );
Session session = entityManager.unwrap(Session.class);
ConnectionReadOnly readOnlyWork = new ConnectionReadOnly();
try{
session.doWork(readOnlyWork);
return pjp.proceed();
} finally {
readOnlyWork.switchBack();
}
}
return pjp.proceed();
}
}
So in the above code I am using a pointcut like following
#Pointcut("this(org.springframework.data.repository.Repository)")
public void inRepositoryLayer() {}
and what it does is
any join point (method execution only in Spring AOP) where the proxy implements the Repository interface
You can have a look it at
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html
Now all my repository read query methods either start with a prefix "findByXXX"(default spring-data-jpa readable method) or "readXXX"(custom read method with #Query annotation) which in my around method executions matched by the above pointcut. According to my requirement I am setting the JDBC Connection readOnly true.
Session session = entityManager.unwrap(Session.class);
ConnectionReadOnly readOnlyWork = new ConnectionReadOnly();
And my ConnectionReadOnly look like following
package com.xyz.forum.replication;
import org.hibernate.jdbc.Work;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Created by Bhupati Patel on 04/11/15.
*/
public class ConnectionReadOnly implements Work {
private Connection connection;
private boolean autoCommit;
private boolean readOnly;
#Override
public void execute(Connection connection) throws SQLException {
this.connection = connection;
this.autoCommit = connection.getAutoCommit();
this.readOnly = connection.isReadOnly();
connection.setAutoCommit(false);
connection.setReadOnly(true);
}
//method to restore the connection state before intercepted
public void switchBack() throws SQLException{
connection.setAutoCommit(autoCommit);
connection.setReadOnly(readOnly);
}
}
So above settings work for my requirement.
it seems that #Pointcut && #Around should be declared in some way like follows:
#Pointcut(value = "execution(public * *(..))")
public void anyPublicMethod() {
}
#Around("#annotation(readOnlyConnection)")

Managing transactions of dynamically created objects in spring

I have a web service which receives a data object(Let's call the class Student). At the web service, I wrap it using a StudentWrapper object as follows
new StudentWrapper(student)
and I want the StudentWrapper class to have methods such as save which would save the data to the database. I want to use the spring framework to annotate the save method so that it will run within a transaction. But then the StudendWrapper object would have to be a spring bean(defined in XML). If it is a spring bean, then I won't be instantiating it as I have shown above.
My question is how can I make the StudentWrapper a Spring bean (so that I can use Spring annotations to manage the transactions) but pass the Student object (that I receive over the web service) in to the StudentWrapper?
If there are any other suggestions that would help me in solving this problem, please share them as well.
If you really want to create the object using a constructor, make the StudentWrapper #Configurable and read up about using AspectJ to create prototype bean definitions for domain objects (section 9.8 of the reference manual.)
A simpler alternative, if you don't want to go with AspectJ but don't want a direct dependency on Spring is to encapsulate the prototype bean creation in a factory. I'll show you using JavaConfig, though you can do something similar in XML.
First the student object...
package internal;
public class Student {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public String toString() {
return "Student{name='" + name + "'}";
}
}
And now the wrapper object...
package internal;
public class StudentWrapper {
private Student student;
public StudentWrapper(Student student) {
this.student = student;
}
public Student getStudent() {
return student;
}
#Override
public String toString() {
return "StudentWrapper{student='" + student + "'} " + super.toString();
}
}
And now the factory,
package internal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
#Component
public class StudentWrapperFactory {
#Autowired
private ApplicationContext applicationContext;
public StudentWrapper newStudentWrapper(Student student) {
return (StudentWrapper) this.applicationContext.getBean("studentWrapper", student);
}
}
And now the JavaConfig, equivalent to an XML configuration
package internal;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
#Configuration
#ComponentScan(basePackages = "internal")
public class FooConfig {
#Bean
#Scope("prototype")
public StudentWrapper studentWrapper(Student student) {
return new StudentWrapper(student);
}
}
Finally the unit test...
package internal;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {FooConfig.class})
public class FooIntegrationTest {
#Autowired
private StudentWrapperFactory studentWrapperFactory;
#Test
public void foo() {
Student student1 = new Student("student 1");
Student student2 = new Student("student 2");
StudentWrapper bean1 = this.studentWrapperFactory.newStudentWrapper(student1);
StudentWrapper bean2 = this.studentWrapperFactory.newStudentWrapper(student2);
System.out.println(bean1);
System.out.println(bean2);
}
}
produces
StudentWrapper{student='Student{name='student 1'}'} internal.StudentWrapper#1b0fa7ff
StudentWrapper{student='Student{name='student 2'}'} internal.StudentWrapper#20de643a
As you can see from the object references of StudentWrapper, they're different prototype beans. #Transactional methods should work as expected in StudentWrapper.

Resources