I'm new to springboot and has been working on a project. I'm using Springboot with hibernate, JPA and hikariCP for mysql DB operations. Currently i have two datasources in which i could not rollback a transaction which i written inside a service. I tried everything i could find, but i couldn't find out what am i missing. Any help would be appreciated and pardon my mistakes and i'm very open to suggestions. Thank you.
#SpringBootApplication
public class AdminModuleApplication {
public static void main(String[] args)
{
SpringApplication.run(AdminModuleApplication.class, args);
}
}
#Configuration
#PropertySource({ "classpath:acc_payDB_properties.properties" })
#EnableJpaRepositories(
basePackages = "com.pinnacle.accpay.dao",
entityManagerFactoryRef = "accpayEntityManager",
transactionManagerRef = "accpayTransactionManager"
)
public class acc_payDBConfig
{
#Autowired
private Environment env;
#Bean
public LocalContainerEntityManagerFactoryBean accpayEntityManager()
{
String packageList[]= {"com.pinnacle.accpay.model","com.pinnacle.admin.model"};
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(accpayDataSource());
em.setPackagesToScan(packageList);
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
Properties jpaProperties = new Properties();
jpaProperties.setProperty("hibernate.hbm2ddl.auto", "none");
Properties jpaProperties2 = new Properties();
jpaProperties2.setProperty("connection.provider_class","org.hibernate.hikaricp.internal.HikariCPConnectionProvider");
em.setJpaProperties(jpaProperties2);
return em;
}
#Bean
public DataSource accpayDataSource()
{
HikariDataSource dataSource = new HikariDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.driver-class-name"));
dataSource.setJdbcUrl(env.getProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.username"));
dataSource.setPassword(env.getProperty("jdbc.password"));
dataSource.setCatalog("acc_pay");
//HikariCP specific properties. Remove if you move to other connection pooling library.
dataSource.setConnectionTimeout(20000);
dataSource.setMaximumPoolSize(20);
dataSource.setMinimumIdle(10);
dataSource.setIdleTimeout(20000);
dataSource.setMaxLifetime(40000);
dataSource.setAutoCommit(false);
dataSource.addDataSourceProperty("cachePrepStmts", true);
dataSource.addDataSourceProperty("prepStmtCacheSize", 25000);
dataSource.addDataSourceProperty("prepStmtCacheSqlLimit", 20048);
dataSource.addDataSourceProperty("useServerPrepStmts", true);
dataSource.addDataSourceProperty("initializationFailFast", true);
dataSource.setPoolName("ACCPAY DB_HIKARICP_CONNECTION_POOL");
dataSource.addDataSourceProperty("useLocalTransactionState", false);
return new HikariDataSource(dataSource);
}
#Bean
public PlatformTransactionManager accpayTransactionManager()
{
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(accpayEntityManager().getObject());
return transactionManager;
}
}
#Service
public class TranscationReceptionService {
#Autowired
PO_TXN_MasterBeanRepository po_TXN_MasterBeanRepository;
#Autowired
GRN_TXN_MasterBeanRepository grn_TXN_MasterBeanRepository;
#Autowired
Invoice_TXN_MasterBeanRepository invoice_TXN_MasterBeanRepository;
#Autowired
POEventLogRepository poEventLogRepository;
#Autowired
InvoiceEventLogRepository invoiceEventLogRepository;
#Autowired
GRNEventlogRepository grnEventlogRepository;
#Autowired
PO_GRN_INVBeanRepository po_GRN_INVBeanRepository;
#Autowired
LinkEventLogBeanRepository linkEventLogBeanRepository;
#Autowired
ScheduledJob scheudledJob;
#Autowired
acc_payDBConfig acc_paydbConfig;
#Value("${in_process_status}")
private String in_process_status;
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void updateMatchStatus(Long poId, Long invoiceId, String poStatus, String invoiceStatus)
{
try
{
po_TXN_MasterBeanRepository.setPOStatusAfterMatching(poStatus, poId, invoiceId);
POEventLogBean poEventLogBean = new POEventLogBean();
poEventLogBean.setPo_id(poId); poEventLogBean.setEvent_time(LocalDateTime.now().toString());
poEventLogBean.setStatus(poStatus);
poEventLogRepository.save(poEventLogBean);
po_GRN_INVBeanRepository.setInvoiceNumber(poId, invoiceId);
Long linkId = po_GRN_INVBeanRepository.getLinkIdfromPONumber(poId);
LinkEventLogBean linkEventLogBean=new LinkEventLogBean();
linkEventLogBean.setLink_id(linkId);
linkEventLogBean.setEvent_time(LocalDateTime.now().toString());
linkEventLogBean.setStatus("");
linkEventLogBeanRepository.save(linkEventLogBean);
invoice_TXN_MasterBeanRepository.setInvoiceStatusAfterMatching(poId, invoiceStatus, invoiceId);
InvoiceEventLogBean invoiceEventLogBean=new InvoiceEventLogBean();
invoiceEventLogBean.setInv_id(invoiceId); invoiceEventLogBean.setEvent_time(LocalDateTime.now().toString());
invoiceEventLogBean.setStatus(invoiceStatus);
invoiceEventLogRepository.save(invoiceEventLogBean);
}
catch (Exception e)
{
}
}
}
#Transactional
#Repository
public interface PO_TXN_MasterBeanRepository extends JpaRepository<PO_TXN_MasterBean, Long>
{
#Transactional
#Modifying(clearAutomatically = true)
#Query("UPDATE PO_TXN_MasterBean p SET p.status= :status, p.inv_id= :invId WHERE p.po_id = :pId")
public void setPOStatusAfterMatching(#Param("status") String status, #Param("pId") Long poId, #Param("invId") Long invoiceId);
}
#Transactional
#Repository
public interface POEventLogRepository extends JpaRepository<POEventLogBean, Integer>
{
}
#Transactional
#Repository
public interface PO_GRN_INVBeanRepository extends JpaRepository<PO_GRN_INVBean, Long>
{
#Transactional
#Modifying(clearAutomatically = true)
#Query("UPDATE PO_GRN_INVBean p SET p.inv_id= :invId WHERE p.po_id = :pId")
public void setInvoiceNumber(#Param("pId") Long poId, #Param("invId") Long invoiceId);
#Query("SELECT p.link_id FROM PO_GRN_INVBean p WHERE p.po_id = :pId")
public Long getLinkIdfromPONumber(#Param("pId") Long poId);
}
#Transactional
#Repository
public interface LinkEventLogBeanRepository extends JpaRepository<LinkEventLogBean, Long>
{
}
#Repository
#Transactional
public interface Invoice_TXN_MasterBeanRepository extends JpaRepository<Invoice_TXN_MasterBean, Long>
{
#Modifying(clearAutomatically = true)
#Transactional
#Query("UPDATE Invoice_TXN_MasterBean i SET i.po_id = :pid, i.status =:status WHERE i.inv_id = :inid")
public void setInvoiceStatusAfterMatching(#Param("pid") Long pid, #Param("status") String status, #Param("inid") Long inId);
}
#Repository
public interface InvoiceEventLogRepository extends JpaRepository<InvoiceEventLogBean, Integer>
{
}
Transaction Rollback is not working as expected because the exception is handled with a try-catch block within TranscationReceptionService.updateMatchStatus(). This prevents the spring framework to be aware of the exception raised and hence does not result in rollback.
Related
Issue
I've to create a Spring batch project with two jobs that can be executed independently and together. Each job has the necessary code to read from database and to write using FlatFileItemWriter and ClassifierCompositeItemWriter. I've found that if I execute the Jobs independently (-Dspring.batch.job.names=schoolJob,-Dspring.batch.job.names=studentJob), the files are generated fine, but when I execute the Jobs together (-Dspring.batch.job.names=schoolJob,studentJob), the files of a Job only have the footer and the header. There seems to be something wrong but I can't find the cause.
Some code
Batch config, job and steps
#Configuration
#EnableBatchProcessing
#SuppressWarnings("rawtypes, unchecked")
public class MyJobConfiguration
{
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Autowired
private JdbcTemplate jdbcTemplate;
#Autowired
private ConfigurableApplicationContext applicationContext;
#Bean
public Step studentStep1() {
return stepBuilderFactory.get("calculateDistinctValuesAndRegisterStudentWriters")
.tasklet(new DynamicStudentWritersConfigurationTasklet(jdbcTemplate,
applicationContext))
.build();
}
#Bean
public Step schoolStep1() {
return stepBuilderFactory.get("calculateDistinctValuesAndRegisterSchoolWriters")
.tasklet(new DynamicSchoolWritersConfigurationTasklet(jdbcTemplate,
applicationContext))
.build();
}
#Bean
#JobScope
public Step studentStep2(StudentReader reader,
#Qualifier("studentClassfierItemWriter")
ClassifierCompositeItemWriter<Student> writer) {
SimpleStepBuilder<Student, Student> studentStep2 = stepBuilderFactory.get(
"readWriteStudents").<Student, Student>chunk(2).reader(reader).writer(writer);
Map<String, FlatFileItemWriter> beansOfType = applicationContext.getBeansOfType(
FlatFileItemWriter.class);
for (FlatFileItemWriter flatFileItemWriter : beansOfType.values())
{
studentStep2.stream(flatFileItemWriter);
}
return studentStep2.build();
}
#Bean
#JobScope
public Step schoolStep2(SchoolReader reader,
#Qualifier("schoolClassfierItemWriter")
ClassifierCompositeItemWriter<School> writer) {
SimpleStepBuilder<School, School> schoolStep2 = stepBuilderFactory.get("readWriteSchools")
.<School, School>chunk(2)
.reader(reader)
.writer(writer);
Map<String, FlatFileItemWriter> beansOfType = applicationContext.getBeansOfType(
FlatFileItemWriter.class);
for (FlatFileItemWriter flatFileItemWriter : beansOfType.values())
{
schoolStep2.stream(flatFileItemWriter);
}
return schoolStep2.build();
}
#Bean
public Job studentJob(Step studentStep1, Step studentStep2) {
return jobBuilderFactory.get("studentJob").start(studentStep1).next(studentStep2).build();
}
#Bean
public Job schoolJob(Step schoolStep1, Step schoolStep2) {
return jobBuilderFactory.get("schoolJob").start(schoolStep1).next(schoolStep2).build();
}
Data source configuration
#Configuration
class DatasourceConfig
{
#Bean
public DataSource dataSource()
{
String dbSchema = "/org/springframework/batch/core/schema-h2.sql";
String initData = "data.sql";
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
.addScript(dbSchema)
.addScript(initData)
.build();
}
}
Readers
#Component
class SchoolReader extends JdbcCursorItemReader<School>
{
#Autowired
private DataSource dataSource;
#Override
public void afterPropertiesSet() throws Exception
{
super.setName("schoolItemReader");
super.setDataSource(dataSource);
super.setSql("select * from school");
super.setRowMapper(new BeanPropertyRowMapper<>(School.class));
super.afterPropertiesSet();
}
}
#Component
class StudentReader extends JdbcCursorItemReader<Student>
{
#Autowired
private DataSource dataSource;
#Override
public void afterPropertiesSet() throws Exception
{
super.setName("studentItemReader");
super.setDataSource(dataSource);
super.setSql("select * from student");
super.setRowMapper(new BeanPropertyRowMapper<>(Student.class));
super.afterPropertiesSet();
}
}
Writers
#Configuration
public class SchoolWriter
{
#Autowired
private ConfigurableApplicationContext applicationContext;
#Bean(name = "schoolClassfierItemWriter")
#StepScope
public ClassifierCompositeItemWriter<School> itemWriter()
{
Map<String, FlatFileItemWriter> beansOfType = applicationContext.getBeansOfType(
FlatFileItemWriter.class);
Classifier<School, FlatFileItemWriter<School>> classifier = school -> beansOfType.get(
"school-group" + school.getGroupId() + "Writer");
return new ClassifierCompositeItemWriterBuilder().classifier(classifier).build();
}
}
#Configuration
public class StudentWriter
{
#Autowired
private ConfigurableApplicationContext applicationContext;
#Bean(name = "studentClassfierItemWriter")
#StepScope
public ClassifierCompositeItemWriter<Student> itemWriter()
{
Map<String, FlatFileItemWriter> beansOfType = applicationContext.getBeansOfType(
FlatFileItemWriter.class);
Classifier<Student, FlatFileItemWriter<Student>> classifier = student -> beansOfType.get(
"student-group" + student.getGroupId() + "Writer");
return new ClassifierCompositeItemWriterBuilder().classifier(classifier).build();
}
}
Tasklets
class DynamicSchoolWritersConfigurationTasklet implements Tasklet
{
private JdbcTemplate jdbcTemplate;
private ConfigurableApplicationContext applicationContext;
public DynamicSchoolWritersConfigurationTasklet(JdbcTemplate jdbcTemplate,
ConfigurableApplicationContext applicationContext)
{
this.jdbcTemplate = jdbcTemplate;
this.applicationContext = applicationContext;
}
#Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext)
{
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
String sql = "select distinct(groupId) from school";
List<Integer> groups = jdbcTemplate.queryForList(sql, Integer.class);
for (Integer group : groups)
{
String name = "school-group" + group + "Writer";
//#f:off
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.addPropertyValue("name", name);
propertyValues.addPropertyValue("lineAggregator", new PassThroughLineAggregator<>());
propertyValues.addPropertyValue("resource", new FileSystemResource("school-" + group + ".txt"));
propertyValues.addPropertyValue("headerCallback", (FlatFileHeaderCallback) writer -> writer.write("header-school"));
propertyValues.addPropertyValue("footerCallback", (FlatFileFooterCallback) writer -> writer.write("footer-school"));
//#f:on
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName(FlatFileItemWriter.class.getName());
beanDefinition.setPropertyValues(propertyValues);
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
registry.registerBeanDefinition(name, beanDefinition);
}
return RepeatStatus.FINISHED;
}
}
class DynamicStudentWritersConfigurationTasklet implements Tasklet
{
private JdbcTemplate jdbcTemplate;
private ConfigurableApplicationContext applicationContext;
public DynamicStudentWritersConfigurationTasklet(JdbcTemplate jdbcTemplate,
ConfigurableApplicationContext applicationContext)
{
this.jdbcTemplate = jdbcTemplate;
this.applicationContext = applicationContext;
}
#Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext)
{
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
String sql = "select distinct(groupId) from student";
List<Integer> groups = jdbcTemplate.queryForList(sql, Integer.class);
for (Integer group : groups)
{
String name = "student-group" + group + "Writer";
//#f:off
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.addPropertyValue("name", name);
propertyValues.addPropertyValue("lineAggregator", new PassThroughLineAggregator<>());
propertyValues.addPropertyValue("resource", new FileSystemResource("student-" + group + ".txt"));
propertyValues.addPropertyValue("headerCallback", (FlatFileHeaderCallback) writer -> writer.write("header-student"));
propertyValues.addPropertyValue("footerCallback", (FlatFileFooterCallback) writer -> writer.write("footer-student"));
//#f:on
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName(FlatFileItemWriter.class.getName());
beanDefinition.setPropertyValues(propertyValues);
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
registry.registerBeanDefinition(name, beanDefinition);
}
return RepeatStatus.FINISHED;
}
}
DAO
#Getter
#Setter
#ToString
#NoArgsConstructor
#AllArgsConstructor
public class School
{
private int id;
private String name;
private int groupId;
}
#Getter
#Setter
#ToString
#NoArgsConstructor
#AllArgsConstructor
public class Student
{
private int id;
private String name;
private int groupId;
}
This is similar to https://stackoverflow.com/a/67635289/5019386. I think you need to make your dynamic item writers step-scoped as well, something like:
propertyValues.addPropertyValue("scope", "step");
Please note that I did not try that. That said, I would really recommend making your app do one thing and do it well, ie isolate job definitions and package/run each job separately.
I am trying to connect two database using spring boot JPA
Application.properties :
Application configuration.
server.port=8102
# Hibernate configuration.
spring.jpa.show-sql = true
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
#spring.jpa.hibernate.ddl-auto = create-drop
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.SQLServer2012Dialect
app.datasource.cardHolder.url=jdbc:sqlserver://server:22;databaseName=DB1
app.datasource.cardHolder.username=name
app.datasource.cardHolder.password=psw
app.datasource.cardHolder.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
server.path=F:\\
app.datasource.card.url=jdbc:sqlserver://server:22;databaseName=DB2
app.datasource.card.username=uname
app.datasource.card.password=psw
app.datasource.card.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.jpa.open-in-view=false
# Logging configuration.
logging.level.com.springboot.storedprocedure=DEBUG
logging.pattern.console= %d{yyyy-MM-dd HH:mm:ss} - %msg%n
com.app.configuration
CardConfig :
#Configuration
#EnableJpaRepositories(basePackages = "com.app.repository",
entityManagerFactoryRef = "cardEntityManagerFactory",
transactionManagerRef= "cardTransactionManager")
public class CardDataSourceConfiguration {
#Bean
#Primary
#ConfigurationProperties("app.datasource.card")
public DataSourceProperties cardDataSourceProperties() {
return new DataSourceProperties();
}
#Primary
#Bean
#ConfigurationProperties("app.datasource.card.configuration")
public DataSource cardDataSource() {
return cardDataSourceProperties().initializeDataSourceBuilder()
.type(BasicDataSource.class).build();
}
// #PersistenceContext(unitName = "first")
#Primary
#Bean(name = "cardEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean cardEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(cardDataSource())
.packages(Card.class)
.build();
}
#Primary
#Bean
public PlatformTransactionManager cardTransactionManager(
final #Qualifier("cardEntityManagerFactory") LocalContainerEntityManagerFactoryBean cardEntityManagerFactory) {
return new JpaTransactionManager(cardEntityManagerFactory.getObject());
}
}
CardHolder :
#Configuration
#EnableJpaRepositories(basePackages = "com.app.repository2",
entityManagerFactoryRef = "cardHolderEntityManagerFactory",
transactionManagerRef= "cardHolderTransactionManager")
public class CardHolderDataSourceConfiguration {
#Bean
#ConfigurationProperties("app.datasource.cardholder")
public DataSourceProperties cardHolderDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties("app.datasource.cardholder.configuration")
public DataSource cardholderDataSource() {
return cardHolderDataSourceProperties().initializeDataSourceBuilder()
.type(BasicDataSource.class).build();
}
// #PersistenceContext(unitName = "second")
#Bean(name = "cardHolderEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean cardHolderEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(cardholderDataSource())
.packages(CardHolder.class)
.build();
}
#Bean
public PlatformTransactionManager cardHolderTransactionManager(
final #Qualifier("cardHolderEntityManagerFactory") LocalContainerEntityManagerFactoryBean cardHolderEntityManagerFactory) {
return new JpaTransactionManager(cardHolderEntityManagerFactory.getObject());
}
}
CardConfi is set the JPARepository properly and return data based on DB2 but same way i have added EntityManger in CardHolderRepository like below,
#Entity
#NamedStoredProcedureQueries(value= {
#NamedStoredProcedureQuery(name= "callSP", procedureName= "cardmsSP", parameters= {
#StoredProcedureParameter(mode= ParameterMode.IN, name= "Name", type= String.class)
})
})
public class CardHolder implements Serializable {
//getter setter
}
com.app.repository2
#Repository
public class CardHolderDao {
#Autowired
SPRepository spRep;
#PersistenceContext
private EntityManager em;
#SuppressWarnings("unchecked")
public List<CardHolder> gecardtHolderList (String input) {
return em.createNamedStoredProcedureQuery("cardmsSP").setParameter("Name", input).getResultList();
}
}
com.app.repository2
public interface SPRepository extends JpaRepository<CardHolder, Long>, JpaSpecificationExecutor<CardHolder>{
}
Problem is that EntityManager em is always pointing to DB2 instead of DB1. where am I doing mistake?
You should set EntityManagerFactoryBuilder#persistenceUnit
#Bean(name = "cardHolderEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean cardHolderEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(cardholderDataSource())
.packages(CardHolder.class)
.persistenceUnit("cardHolderEntityManager") // !!!
.build();
}
And then
#PersistenceContext(unitName = "cardHolderEntityManager")
EntityManager em;
I am new to JDBC template and am trying to use prepared statement for inserting data into database using auto commit mode off for achieving high performance but at the end i'm not able to commit the transaction. Please suggest some correct approach or reference that might solve my problem.
Thanks in advance...
SpringjdbcApplication.java
#SpringBootApplication
public class SpringjdbcApplication
{
public static void main(String[] args)
{
ApplicationContext context = SpringApplication.run(SpringjdbcApplication.class, args);
SampleService service = context.getBean(SampleService.class);
List<Batch> batchList = new ArrayList<>();
batchList.add(new Batch("A","B"));
batchList.add(new Batch("B","B"));
batchList.add(new Batch("C","B"));
batchList.add(new Batch("D","B"));
batchList.add(new Batch("E","B"));
System.err.println("The number of rows inserted = "+service.singleInsert(batchList));
System.err.println("The count of batch class is = "+service.getCount());
}
}
SampleConfiguration.java
#Configuration
public class SampleConfiguration
{
#Bean
public DataSource mysqlDataSource()
{
HikariConfig config= new HikariConfig();
config.setDriverClassName("ClassName");
config.setJdbcUrl("URL");
config.setUsername("User");
config.setPassword("Password");
config.setMinimumIdle(600);
config.setMaximumPoolSize(30);
config.setConnectionTimeout(251);
config.setMaxLifetime(250);
config.setAutoCommit(false);
return new HikariDataSource(config);
}
#Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource)
{
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
}
Batch.java
#Entity
public class Batch implements Serializable
{
private static final long serialVersionUID = -5687736664713755991L;
#Id
#Column(name="field1")
private String field1;
#Column(name="field2")
private String field2;
....... getter, setter and constructor
}
SampleService.java
#Service
public interface SampleService
{
public int singleInsert(List<Batch> batchList);
}
SampleServiceImpl.java
#Service
public class SampleServiceImpl implements SampleService
{
#Autowired
JdbcTemplate jdbcTemplate;
#Override
public int singleInsert(List<Batch> batchList)
{
for(Batch i:batchList)
{
jdbcTemplate.update("insert into batch values(?,?)",i.getField1(),i.getField2());
}
try
{
DataSourceUtils.getConnection(jdbcTemplate.getDataSource()).commit();
}
catch(Exception e)
{
e.printStackTrace();
}
return 1;
}
}
I am using JPA in my application, and it works once I query for objects, however it throw error javax.persistence.TransactionRequiredException: No transactional EntityManager available once I tried to save or update an object.
This is the java configuration:
#Configuration
#EnableTransactionManagement(proxyTargetClass = true)
#PropertySource("classpath:dao.properties")
public class JpaConfig {
#Autowired
private Environment env;
#Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
.....................
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.dialect", ...........)
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setJpaProperties(jpaProperties);
entityManagerFactoryBean.setPackagesToScan("com....");
return entityManagerFactoryBean;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
}
Note I use proxyTargetClass = true in the #EnableTransactionManagement, since I do not want to create the useless interfaces in my application.
And this is the concrete implemention of the dao:
#Transactional
#Repository
public abstract class AbstractJPADao<I, E> {
#Autowired
#PersistenceContext
protected EntityManager entityManager;
private Class<E> type;
public AbstractJPADao() {
type=....
}
#Override
public Result<E> find(I id) {
E e = entityManager.find(type, id);
return Result.newInstance().setContent(e);
}
#Override
public Result<E> find(Map<String, Object> condition) {
Query q = entityManager.createQuery(".......));
return Result.newInstance().setContent(q.getResultList());
}
#Override
public E save(E element) {
entityManager.persist(element);
return element;
}
#Override
public E update(E element) {
entityManager.merge(element);
return element;
}
#Override
public void delete(E element) {
entityManager.remove(element);
}
}
#Repository
#Transactional
public class DepartmentDao extends AbstractJPADao<String, Department> {
#Override
protected String selectCause(Map<String, Object> condition) {
return "";
}
}
And the controller as the client of the dao:
#Controller
#RequestMapping("/api/deps")
public class DepartmentCtrl {
#Autowired
private DepartmentDao departmentDao;
#RequestMapping(value = "", method = RequestMethod.POST)
public Result create(#Valid Department department, BindingResult bindingResult) {
if (!bindingResult.hasErrors()) {
departmentDao.save(department);
return Result.newInstance().setContent(department);
}
throw new RuntimeException("...");
}
}
Is there anything wrong?
dao.properties:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/proj
jdbc.username=root
jdbc.password=
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.hbm2ddl.auto=update
#hibernate.ejb.naming_strategy=true
hibernate.show_sql=true
hibernate.format_sql=true
Try renaming method transactionManager to txManager in the class JpaConfig
Autowiring goes by the name txManager
Edit
Also the framework could be expecting a no argument method for txManager. Can you try changing to
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
As noticed Tiny, you have two annotations #Autowired and #PersistenceContext over the protected field of type EntityManager in AbstractJPADao.
Try removing #Autowired. #PersistenceContext is enough to inject the EntityManager.
I'm using spring-jpa. I have 2 tests.
#Test
#Transactional
public void testFindAll() {
List<Engine> eList = engineService.findAll();
Engine e = eList.get(0); //this engine id=3
List<Translation> tList = e.getTranslations();
for(Translation t : tList) {
...
}
}
This method fails with this exception:
org.hibernate.LazyInitializationException: failed to lazily initialize
a collection of role: xxx.Engine.translations, could not initialize
proxy - no Session
However, this method works just fine:
#Test
#Transactional
public void testFindOne() {
Engine e = engineService.findOne(3);
List<Translation> tList = e.getTranslations();
for(Translation t : tList) {
...
}
}
Why translation list is successfully loaded in one case, but not in another?
EDIT: service/repo code:
public interface EngineRepository extends JpaRepository<Engine, Integer>
{
}
.
#Service
#Transactional
public class EngineService
{
#Autowired
private EngineRepository engineRepository;
public List<Engine> findAll()
{
return engineRepository.findAll();
}
public Engine findOne(Integer engId)
{
return engineRepository.findOne(engId);
}
}
.
public class Engine implements Serializable {
...
#OneToMany
#JoinColumn(name="ID", referencedColumnName="TRAN_ID", insertable=false, updatable=false, nullable=true)
#LazyCollection(LazyCollectionOption.EXTRA)
private List<Translation> translations;
...
}
Config:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = {"xxx.dao"})
#ComponentScan(basePackages = {"xxx.dao", "xxx.service", "xxx.bean"})
#PropertySource("classpath:application.properties")
public class SpringDataConfig {
#Autowired
private Environment environment;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(environment.getProperty("db.url"));
dataSource.setDriverClassName(environment.getProperty("db.driverClass"));
dataSource.setUsername(environment.getProperty("db.username"));
dataSource.setPassword(environment.getProperty("db.password"));
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setDatabase(Database.POSTGRESQL);
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getProperty("hibernate.showSQL"));
properties.put("hibernate.format_sql", environment.getProperty("hibernate.formatSQL"));
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPackagesToScan("xxx.model");
entityManagerFactoryBean.setJpaVendorAdapter(hibernateJpaVendorAdapter);
entityManagerFactoryBean.setJpaProperties(properties);
return entityManagerFactoryBean;
}
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
I think the problem here is that the session gets closed after the first line in the first case. You should check out the JpaRepository implementation of findAll().
Integration Testing with Spring
It seems you're failing to provide a Spring Context within your TestCase, what that means? The #Transactional is being ignored. Therefore you end up with the closed session exception, because there is no transaction.
Take a look how to configure a TestCase with a Spring Context here
#RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from AppConfig and TestConfig
#ContextConfiguration(classes = {AppConfig.class, TestConfig.class})
public class MyTest {
#Autowired
EngineService engineService;
#Test
#Transactional
public void testFindOne() {}
#Test
#Transactional
public void testFindAll() {}
}