Recently, I integrated Spring into my Jboss application. I am also putting together a standalone application that will use some of the Spring service classes. Here is my Standalone source:
#Component
public class StandaloneTest {
public static ApplicationContext context;
public static void main(String[] args) {
context = new AnnotationConfigApplicationContext(StandaloneSpringBeanConfiguration.class);
StandaloneTest test = context.getBean(StandaloneTest.class);
System.out.println(context);
test.startProcess();
}
public StandaloneTest() {
}
/**
* Start the main process
*/
private void startProcess() {
run();
}
private void run() {
TestService testService = context.getBean(TestService.class);
testService.transactionalTest();
System.out.println("finished");
}
Here is the source for TestService:
#Service
public class TestServiceImpl implements TestService {
public GeneralDAO generalDAO;
private EntityManager em;
private EntityManagerFactory emf;
public TestServiceImpl(){}
#Transactional
public void transactionalTest() {
Something something = new Something();
getEntityManager().persist(something);
}
public EntityManager getEntityManager() {
if (em == null) {
emf = Persistence.createEntityManagerFactory("xxx");
em = emf.createEntityManager();
}
return em;
}
}
When I run this method in my Jboss server, the #Transactional annotation works as expected. However, when I run the standalone application, the #Transactional annotations seems to be ignored. I can't figure out why.
If anyone knows what I need to do to get this to work or has any ideas/comments, please share. Thanks!
Update
Here's my Config file:
#Configuration
#ComponentScan
#EnableTransactionManagement
public class StandaloneSpringBeanConfiguration {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceUnitName("myPersistenceContext");
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "xxx.xxx.xxx"});
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
// em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/jboss_test");
dataSource.setUsername( "root" );
dataSource.setPassword( "root" );
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
JpaTransactionManager transactionManager = new JpaTransactionManager(emf);
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
More Updates
#Service
public class TestServiceImpl implements TestService {
public GeneralDAO generalDAO;
#PersistenceContext
private EntityManager em;
private EntityManagerFactory emf;
public TestServiceImpl(){}
#Transactional
public void transactionalTest() {
Something something = new Something();
em.persist(something);
}
}
This is definitely moving me in the right direction. I'm getting this error now: No JTA UserTransaction available - specify either 'userTransaction' or 'userTransactionName' or 'transactionManager' or 'transactionManagerName'
I'm having trouble figuring out the cause, but is the issue my choice of transactionManager? I was reading somewhere that I need to use JOTM or Atomikos for standalone, but I don't think JOTM can be used in Spring 4 and Atomikos requires me to use Maven, which is not an option for me. Using the JTATransactionManager gave me the same result.
Working Solution
The JTA error seemed to resolve itself this morning. Not sure what happened. I then had to make sure that all the necessary packages were being scanned by LocalContainerEntityManagerFactoryBean because I was getting an IllegalArgumentException Unknown Entity
Config
public class StandaloneSpringBeanConfiguration {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "xxx.xxx.xxx", "xxx.xxx.xxx"});
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
// em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/jboss_test");
dataSource.setUsername( "root" );
dataSource.setPassword( "root" );
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
Service Class
#Service
public class TestServiceImpl implements TestService {
public GeneralDAO generalDAO;
#PersistenceContext
private EntityManager em;
private EntityManagerFactory emf;
public TestServiceImpl(){}
#Transactional
public void transactionalTest() {
Something something = new Something();
em.persist(something);
}
}
Transactions are working fine, they simply don't work in your setup. This is because you are working around a transactional bound EntityManager due to the fact you are recreating it.
Basically the code for your TestServiceImpl is flawed. You should remove the getEntityManager() method and instead annotate the EntityManager field with #PersistenceContext.
#Service
public class TestServiceImpl implements TestService {
public GeneralDAO generalDAO;
#PersistenceContext
private EntityManager em;
public TestServiceImpl(){}
#Transactional
public void transactionalTest() {
Something something = new Something();
em.persist(something);
}
}
That is all you should need.
Related
I am upgrading my JBoss server from 5 to 7 and am now incorporating Spring 4. I am having some trouble using Spring's #Transactional annotation. It does not appear to be working. I am also trying to use a java based configuration file instead of an xml file (I believe I can get away without using any xml, but correct me if I am wrong). The problem is that nothing is being saved in my db, leading me to believe that the #Transactional isn't working. Here is my config file:
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration {
#Bean
public FirstTestBean firstTestBean() {
return new FirstTestBean();
}
#Bean
public TestService testService() {
return new TestServiceImpl();
}
#Bean
public SomethingDAO somethingDAO(){
return new SomethingDAOImpl();
}
#Bean
public GeneralDAO generalDAO(){
return new GeneralDAOImpl();
}
Here is a test class with the #Transactional method:
//#RequestScoped
#ManagedBean(name="firstTestBean")
#Component
public class FirstTestBean {
private EntityManager em;
private EntityManagerFactory emf;
#Transactional
public String transactionalTest() {
//ApplicationContext context = new AnnotationConfigApplicationContext(SpringBeanConfiguration.class);
Something something = new Something();
getEntityManager().persist(something);
return "dkljs";
}
public EntityManager getEntityManager() {
if (em == null) {
emf = Persistence.createEntityManagerFactory("xxx");
em = emf.createEntityManager();
}
return em;
}
I am also using Hibernate 4, which is compatible with Spring. I commented out the ApplicationContext because I have that running separately on the start up of JBoss. I was using that earlier to access a bean, but I have since simplified things in order to get the #Transactional working and thus do not need it here. The #ComponentScan does not need parameters because these classes are in the same package.
Any help would be greatly appreciated. Thanks!
Updates
I've made some of the changes suggested. Things appear to be moving in the right direction.
Here are my updated files:
#ManagedBean(name="firstTestBean")
public class FirstTestBean {
public String getTestString() {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringBeanConfiguration.class);
TestService testService = context.getBean(TestService.class);
testService.transactionalTest();
return "adfs";
}
public String test() {
return "firstTestBean works";
}
}
Note - some of these classes will be run outside of the Jboss application server as standalone applications, so for that reason, I am staying away from FacesContext when instantiating the TestService in FirstTestBean, as Spring beans work in standalone, but FacesContext beans do not.
#Component
#Transactional
public class TestServiceImpl implements TestService {
public GeneralDAO generalDAO;
//#Autowired
private EntityManager em;
//#Autowired
private EntityManagerFactory emf;
public TestServiceImpl(){}
public String transactionTest() {
Something something = new Something();
getEntityManager().persist(something);
return "dkljs";
}
#autowired on the EntityManager and EntityManagerFactory did not work - I received an error saying No Qualified Bean of type EntityManager when it was annotated with the #autowired like suggested.
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration implements TransactionManagementConfigurer {
#Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
String hibernatePropsFilePath = "/[path]/hibernate.cfg.xml";
File hibernatePropsFile = new File(hibernatePropsFilePath);
org.hibernate.cfg.Configuration cfg = new org.hibernate.cfg.Configuration().configure(hibernatePropsFile);
SessionFactory sessionFactory = cfg.buildSessionFactory();
HibernateTransactionManager txManager = new HibernateTransactionManager(sessionFactory);
txManager.setNestedTransactionAllowed(true);
return txManager;
}
}
The error I am getting now is:Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.context.event.internalEventListenerProcessor': Initialization of bean failed
; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration':
Injection of autowired dependencies failed; nested exception is org.hibernate.service.UnknownUnwrapTypeException: Cannot unwrap to requested type [javax.sql.DataSource]
I take it this means that the #Transactional is at least being recognized, but I'm having some issues getting this to work. Any further advice would be greatly appreciated.
More Updates
Found this article: http://www.baeldung.com/the-persistence-layer-with-spring-and-jpa#javaconfig
and followed it. My new config file:
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration { //implements TransactionManagementConfigurer {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "xxx.xxx.xxx" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
// em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/jboss_test");
dataSource.setUsername( "root" );
dataSource.setPassword( "root" );
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
Good news is that I am no longer having deployment issues. The logging also suggests that my changes are having some effect on the server. Unfortunately, nothing is being saved, so it is still not quite working.
The first thing that draws my attention is the use of #Transactional and #Component on a ManagedBean.
JSF and Spring are definately made to work together but i never saw them used this way on many projects i was working on.
Im not sure if that is the cause of your problems but please consider changing that.
I would do like this:
a) Define some service layer where you would wrap your calls with transactions and inject the JPA classes:
#Component
#Transactional
class Service{
#Autowired
private EntityManager em;
#Autowired
private EntityManagerFactory emf;
public String serviceMethod(..){ .. }
}
b) Inject that to Jsf's ManagedBean while removing the unnecessary annotations:
#ManagedBean(name="firstTestBean")
public class FirstTestBean {
#ManagedProperty("#{service}")
private Service service;
public String transactionalTest() {
return service.serviceMethod();
}
}
Ok, so I finally got it (Check out my OP for all updates that led me to this point).
Here's my final config file:
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceUnitName("myPersistenceContext");
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "xxx.xxx.xxx" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
// em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/jboss_test");
dataSource.setUsername( "root" );
dataSource.setPassword( "root" );
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
JtaTransactionManager transactionManager = new JtaTransactionManager();
// transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
So first, I added in
em.setPersistenceUnitName("myPersistenceContext");
This gave me the error:
Spring IllegalStateException: A JTA EntityManager cannot use getTransaction()
From here, I did some research (Spring IllegalStateException: A JTA EntityManager cannot use getTransaction())
and changed
JpaTransactionManager transactionManager = new JpaTransactionManager();
to
JtaTransactionManager transactionManager = new JtaTransactionManager();
Also, my persistence.xml file is:
<persistence-unit name="myPersistenceContext">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:jboss/datasources/myDS</jta-data-source>
<class>xxx.xxx.xxx.Something</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.cache.use_second_level_cache" value="true" />
<property name="hibernate.id.new_generator_mappings" value="false"/>
<property name="hibernate.classloading.use_current_tccl_as_parent" value="false"/>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.archive.autodetection" value="class, hbm" />
</properties>
</persistence-unit>
Thanks to Maciej Kowalski for your help - you pushed me in the right direction.
I'm trying to solve a problem with two datasources. I read many text in the internet and made simple solution based on this source
I've made two "config classes", like this:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "ResultEntityManagerFactory",
transactionManagerRef = "ResultEntityTransactionManager",
basePackages = "com.migr.result.repositories"
)
public class ResultTablesConfig {
#Autowired
#Qualifier("orclAdapter")
JpaVendorAdapter jpaVendorAdapter;
#Bean(name="ResultEntityDataSource")
#ConfigurationProperties(prefix = "datasource.migr.result")
public DataSource declReaconDS() {
return new DataSource();
}
#Bean(name = "ResultEntityManager")
public EntityManager entityManager() {
return entityManagerFactory().createEntityManager();
}
#Bean(name = "ResultEntityManagerFactory")
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(declReaconDS());
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("com.migr.result.tables.*");
lef.setPersistenceUnitName("ResultEntityPersistenceUnit");
lef.afterPropertiesSet();
return lef.getObject();
}
#Bean(name = "ResultEntityTransactionManager")
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager(entityManagerFactory());
}
}
and another one, practically same - SourceTablesConfig.
only difference is #Bean names (they all start with "Source") and repositories mapping, like this:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "SourceEntityManagerFactory",
transactionManagerRef = "SourceEntityTransactionManager",
basePackages = "com.migr.source.repositories"
)
public class SourceTablesConfig {
...............
#Bean(name = "SourceEntityManagerFactory")
public EntityManagerFactory entityManagerFactory(){
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(declReaconDS());
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("com.migr.source.tables.*");
lef.setPersistenceUnitName("SourceEntityPersistenceUnit");
lef.afterPropertiesSet();
return lef.getObject();
}
My services are simle:
#Service
public class CatalogsSourceService {
#Autowired
CatalogsSourceRepository catalogsrepository;
.......
}
where CatalogsSourceRepository extends JpaRepository<Catalogs, Long>
My "main" class:
#RestController
#RequestMapping("/")
public class Main {
#Autowired
CatalogsSourceService source_serv;
#Autowired
CatalogsResultService result_serv;
#RequestMapping
public Object index() {
Map<String,List> results = new HashMap<>();
results.put("First", source_serv.findAll());
results.put("Second", source_serv.findAll());
return results;
}
it worked perfectly when I was having only one DataSource,
but now it exits with error:
expected single matching bean but found 2: SourceEntityManagerFactory,ResultEntityManagerFactory
I don't get why it is happening. My solution looks practically like mentioned in above link, but his do not get such error.
I will be very gratefull for any help
PS. forgot about my "AppConfig":
#Configuration
#ComponentScan(basePackages = { "com.migr" })
public class AppConfig {
#Bean(name="orclAdapter")
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setShowSql(true);
jpaVendorAdapter.setDatabase(Database.ORACLE);
return jpaVendorAdapter;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
}
Sorry guys, my problem is solved. I missed easiest solution).
I just had to annotate one EntityManagerFactory bean with #Primary annotation.
#Bean(name = "SourceEntityManagerFactory")
#Primary
public EntityManagerFactory entityManagerFactory() {
...........
}
obvious solutions are not always so obvious...
I'm using Camunda 7.3, Spring 4.2.4 and Hibernate 4.3.8 and I'm trying to use them with the same transaction as explained in Camunda Documentation. The transaction works ok with Hibernate operations but not with Camunda operations, if a transaction rollback occurs just the hibernate operations are reverted.
#Configuration
public class CamundaConfiguration {
// Variables with connection Data
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
bean.setPersistenceUnitName("PostgreSQL");
bean.setDataSource(dataSource());
bean.getJpaPropertyMap().put("hibernate.dialect", "org.hibernate.dialect.PostgreSQL82Dialect");
bean.getJpaPropertyMap().put("hibernate.ejb.naming_strategy", NamingStrategyLowerCase.class.getCanonicalName());
bean.getJpaPropertyMap().put("hibernate.jdbc.batch_size", 0);
bean.getJpaPropertyMap().put("hibernate.cache.use_second_level_cache", true);
bean.getJpaPropertyMap().put("hibernate.cache.use_query_cache", true);
bean.getJpaPropertyMap().put("javax.persistence.sharedCache.mode", SharedCacheMode.ALL);
bean.getJpaPropertyMap().put("hibernate.cache.default_cache_concurrency_strategy", "read-write");
bean.getJpaPropertyMap().put("javax.persistence.validation.factory", validator);
bean.getJpaPropertyMap().put("hibernate.cache.region.factory_class", SingletonEhCacheRegionFactory.class.getCanonicalName());
bean.setPersistenceProviderClass(org.hibernate.jpa.HibernatePersistenceProvider.class);
bean.setPackagesToScan("br.com.model");
return bean;
}
#Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager bean = new JpaTransactionManager(entityManagerFactory());
bean.getJpaPropertyMap().put("org.hibernate.flushMode", FlushMode.AUTO);
bean.setDataSource(dataSource);
bean.setPersistenceUnitName("PostgreSQL");
return bean;
}
#Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setDriverClassName(driverClass);
config.setJdbcUrl(jdbcUrl);
config.setUsername(username);
config.setPassword(password);
config.setMaximumPoolSize(50);
config.setConnectionTestQuery("select 1");
HikariDataSource bean = new HikariDataSource(config);
return new LazyConnectionDataSourceProxy(bean);
}
#Bean
public ManagedProcessEngineFactoryBean processEngine() {
ManagedProcessEngineFactoryBean processEngineFactoryBean = new ManagedProcessEngineFactoryBean();
processEngineFactoryBean.setProcessEngineConfiguration(processEngineConfiguration());
return processEngineFactoryBean;
}
#Bean
public SpringProcessEngineConfiguration processEngineConfiguration() {
SpringProcessEngineConfiguration processEngineConfiguration = new SpringProcessEngineConfiguration();
processEngineConfiguration.setDataSource(dataSource());
processEngineConfiguration.setTransactionManager(transactionManager());
processEngineConfiguration.setJobExecutorActivate(true);
processEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfigurationImpl.DB_SCHEMA_UPDATE_TRUE);
return processEngineConfiguration;
}
#Bean
public TaskService taskService() throws Exception {
return processEngine().getObject().getTaskService();
}
}
The dataSource and transactionManager is the same used by Spring and Hibernate.
#Service
public class TaskManager {
#Inject
private TaskService taskService;
#Transactional
public void completeTask(String taskId, final Map<String, Object> variables) {
org.camunda.bpm.engine.task.Task camundaTask = taskService.createTaskQuery().taskId(taskId).singleResult();
taskService.complete(camundaTask.getId(), variables);
// Hibernate Operations
throw new RuntimeException("Exception test");
}
}
When executed the code above a rollback will occur and the 'Hibernate Operations' are rollbacked but the operations executed in taskService.complete are not.
I already debugged the Camunda code and everything seems ok, I found a SpringTransactionInterceptor and the commands are executed inside a TransactionTemplate.execute() and at this point the transaction is active.
After studying about transactions, Jpa and Spring, I found out the problem was jpaDialect is not configured, it's responsible to synchronize JDBC and JTA transactions.
The dialect object can be used to retrieve the underlying JDBC
connection and thus allows for exposing JPA transactions as JDBC
transactions.
I included the following code into configuration and now it's working:
#Configuration
public class CamundaConfiguration {
....
#Bean
public JpaDialect jpaDialect() {
return new org.springframework.orm.jpa.vendor.HibernateJpaDialect();
}
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
bean.setJpaDialect(jpaDialect());
bean.setJpaVendorAdapter(new org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter());
...
return bean;
}
#Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager bean = new JpaTransactionManager(entityManagerFactory());
bean.setJpaDialect(jpaDialect());
...
return bean;
}
...
}
Here is my Repository configuration:
#Configuration
public class RepositoryConfing {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter){
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
entityManagerFactoryBean.setPackagesToScan("com.imdb.model");
return entityManagerFactoryBean;
}
#Bean
public BasicDataSource dataSource(){
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("org.postgresql.Driver");
ds.setUrl("jdbc:postgresql://localhost:5432/imdb");
ds.setUsername("***");
ds.setPassword("***");
ds.setInitialSize(5);
return ds;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter(){
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.POSTGRESQL);
adapter.setShowSql(true);
adapter.setGenerateDdl(false);
adapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQLDialect");
return adapter;
}
}
When I call merge method, I get an exception: javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'merge' call
May be my RepositoryConfig is lacking of some additional configurations?
EDIT:
My new Repository configuration:
#Configuration
#EnableTransactionManagement
public class RepositoryConfing {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter){
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
entityManagerFactoryBean.setPackagesToScan("com.imdb.model");
return entityManagerFactoryBean;
}
#Bean
public BasicDataSource dataSource(){
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("org.postgresql.Driver");
ds.setUrl("jdbc:postgresql://localhost:5432/imdb");
ds.setUsername("***");
ds.setPassword("***");
ds.setInitialSize(5);
return ds;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter(){
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.POSTGRESQL);
adapter.setShowSql(true);
adapter.setGenerateDdl(false);
adapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQLDialect");
return adapter;
}
#Bean
public PlatformTransactionManager txManager() {
return new DataSourceTransactionManager(dataSource());
}
}
Now it fails with StackOverflow. May be DataSourceTransactionManager is not suit for JPA?
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
Your txManager method should be renamed to transactionManager.
You need to have a JpaTransactionManager instead of a DataSourceTransactionManager
EDIT
In order to have fully functional transaction with JPA you need to :
Use #EnableTransactionManagement on your configuration file
Declare an EntityManagerFactoryBean
Declare a JpaTransactionManager (the bean must be named transactionManager) created with your previously declared EntityManagerFactoryBean
Inject (with #PersistenceContext or #Autowired) an EntityManager in your #Repository (which must be a spring bean)
Call your repository from a #Service and use a public method anotated with #Transactional
Of course this is simplified and I assume you are using java config, annotations, and autocomponent scanning.
I've read I believe tried all of the posts on this, but no luck in finding the right answer.
I am using java based configuration with my spring mvc project, and wanted to try Spring CrudRepository, to get away from DAOs, and that is when the whole hell broke loose:
started with "no transaction is in progress" on flush after persist:
- tried adding #Transactional to the method - none of the variations found here worked
- tried changing configuration, but since it is java based, most of the answers are xml based. no luck either.
So finally I have to ask:
How to configure my project to make CrudRepository persist, or how to create Spring EntityManager using java configuration.
This is the last version of my configuration file:
#Configuration
#ComponentScan(basePackages = { "ba.fit.vms" })
#ImportResource(value = "classpath:spring-security-context.xml")
#EnableTransactionManagement
#EnableJpaRepositories
public class AppConfig {
#Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setLocation(new ClassPathResource("/persistence.properties"));
return ppc;
}
// Security Configuration
#Bean
public KorisnickiServis korisnickiServis(){
return new KorisnickiServis();
}
#Bean
public TokenBasedRememberMeServices rememberMeServices() {
return new TokenBasedRememberMeServices("remember-me-key", korisnickiServis());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new StandardPasswordEncoder();
}
// Jpa Configuration
#Value("${dataSource.driverClassName}")
private String driver;
#Value("${dataSource.url}")
private String url;
#Value("${dataSource.username}")
private String username;
#Value("${dataSource.password}")
private String password;
#Value("${hibernate.dialect}")
private String dialect;
#Value("${hibernate.hbm2ddl.auto}")
private String hbm2ddlAuto;
#Bean
public DataSource configureDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean configureEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(configureDataSource());
entityManagerFactoryBean.setPackagesToScan("ba.fit.vms");
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Properties jpaProperties = new Properties();
jpaProperties.put(org.hibernate.cfg.Environment.DIALECT, dialect);
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_AUTO, hbm2ddlAuto);
//jpaProperties.put(org.hibernate.cfg.Environment.SHOW_SQL, true);
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
#Bean
public PlatformTransactionManager transactionManager() {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(configureEntityManagerFactory().getObject());
return transactionManager;
}
}
I've tried number of variations, but was always receiving same "no transaction is in progress" error.
Also, just a glimpse at the repos:
LokacijaRepository:
#Transactional
public interface LokacijaRepository extends CrudRepository<Lokacija, Long> {
}
And LokacijaRepositoryImpl:
#Repository
public class LokacijaRepositoryImpl implements LokacijaRepository {
protected static Logger logger = Logger.getLogger("repo");
#PersistenceContext // tried this as well(type= PersistenceContextType.EXTENDED)
private EntityManager entityManager;
#Override
#Transactional// tried number of variations here as well, like REQUIRED...
public <S extends Lokacija> S save(S entity) {
logger.debug("trying to save!");
try {
entityManager.persist(entity);
entityManager.flush();
return entity;
} catch (Exception e) {
logger.debug("error: "+ e.toString());
return null;
}
}
If you need anything else to help me figure this one out, let me know.
The problem is that you are attempting to create an implementation of LokacijaRepository (in LokacijaRepositoryImpl) while Spring Data JPA (which you have configured) is trying to do the same.
What you need to do is:
totally remove LokacijaRepositoryImpl
Either change configureEntityManagerFactory to entityManagerFactory or add entityManagerFactoryRef=configureEntityManagerFactory to #EnableJpaRepositories