I have the following:
#Service
public class AsyncMarketService {
#Inject
IdentifierManager identifierManager;
#Transactional
public void getProducts() {
identifierManager.getTitleForIdentifier();
}
}
The service method is being called from an #Async method.
I see in the log that the transaction is being opened:
134331 DEBUG [Company-1] org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'transactionManager'
134342 DEBUG [Company-1] org.springframework.orm.hibernate4.HibernateTransactionManager - Creating new transaction with name [com.estartup.caller.AsyncMarketService.getProducts]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
134343 DEBUG [Company-1] org.springframework.orm.hibernate4.HibernateTransactionManager - Opened new Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList#b9b0793 updates=org.hibernate.engine.spi.ExecutableList#ee6c9e deletions=org.hibernate.engine.spi.ExecutableList#4230dd40 orphanRemovals=org.hibernate.engine.spi.ExecutableList#5d5e00b collectionCreations=org.hibernate.engine.spi.ExecutableList#56a1c8bd collectionRemovals=org.hibernate.engine.spi.ExecutableList#25d8d46b collectionUpdates=org.hibernate.engine.spi.ExecutableList#61c6c74d collectionQueuedOps=org.hibernate.engine.spi.ExecutableList#4b39a6c6 unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
134346 DEBUG [Company-1] org.springframework.orm.hibernate4.HibernateTransactionManager - Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList#b9b0793 updates=org.hibernate.engine.spi.ExecutableList#ee6c9e deletions=org.hibernate.engine.spi.ExecutableList#4230dd40 orphanRemovals=org.hibernate.engine.spi.ExecutableList#5d5e00b collectionCreations=org.hibernate.engine.spi.ExecutableList#56a1c8bd collectionRemovals=org.hibernate.engine.spi.ExecutableList#25d8d46b collectionUpdates=org.hibernate.engine.spi.ExecutableList#61c6c74d collectionQueuedOps=org.hibernate.engine.spi.ExecutableList#4b39a6c6 unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
134346 DEBUG [Company-1] org.springframework.jdbc.datasource.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/Company?zeroDateTimeBehavior=convertToNull]
134377 DEBUG [Company-1] org.springframework.orm.hibernate4.HibernateTransactionManager - Exposing Hibernate transaction as JDBC transaction [com.mysql.jdbc.JDBC4Connection#4d9068ae]
However, I still get an error:
Caused by: java.util.concurrent.ExecutionException: org.hibernate.HibernateException: get is not valid without active transaction
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
when I am trying to call from that method (identifierManager.getTitleForIdentifier();) the DAO to retrieve something by id.
What is wrong?
EDITED
Configuration Bean:
#Configuration
#ComponentScan({ "com.estartup" })
#PropertySource("classpath:jdbc.properties")
#EnableScheduling
#EnableTransactionManagement
public class PersistenceConfig {
#Autowired
Environment env;
#Bean
public DataSource dataSource() {
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource(env.getProperty("connection.url"), env.getProperty("connection.username"),
env.getProperty("connection.password"));
driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
return driverManagerDataSource;
}
public PersistenceConfig() {
super();
}
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean factoryBean = null;
try {
factoryBean = createBaseSessionFactory();
factoryBean.getHibernateProperties().setProperty("hibernate.current_session_context_class", "thread");
} catch (Exception e) {
e.printStackTrace();
}
return factoryBean;
}
private LocalSessionFactoryBean createBaseSessionFactory() throws IOException {
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
Properties pp = new Properties();
pp.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
pp.setProperty("hibernate.max_fetch_depth", "3");
pp.setProperty("hibernate.current_session_context_class", "org.springframework.orm.hibernate4.SpringSessionContext");
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan(new String[] { "com.estartup.*" });
factoryBean.setHibernateProperties(pp);
return factoryBean;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
}
One thing you could try is to drop the #Transactional annotation and manually open a session yourself (not sure if there is a better way...)
#Service
public class AsyncMarketService {
#Inject
IdentifierManager identifierManager;
#Inject
SessionFactory sessionFactory;
public void getProducts() {
Session session = sessionFactory.openSession();
Transaction tx = null;
try {
identifierManager.getTitleForIdentifier();
} catch(Exception e) {
if(tx != null) tx.rollback();
} finally {
session.close();
}
}
}
If you find yourself doing this more than once, maybe write another component that has a method that takes a closure or something.
If I find a more elegant way in the next few minutes, I'll keep you posted.
Try moving the #Transactional to the calling asynchronous method. The transaction does not get propagated through the call hierarchy from one Spring component to the other, because the asynchronous method is being scheduled and executed at a later time by a task executor.
Related
I'm having a really hard time setting up a jBPM7 project. I'm trying to use the jBPM engine with an H2 in-memory database to make use of the Human Task Service. I've set up my data source, entity manager factory and transaction manager as below:
application.properties
...
jbpm.datasource.jdbc-url=jdbc:h2:mem:testdb
jbpm.datasource.username=sa
jbpm.datasource.password=
...
JbpmDataConfiguration.java
#Configuration
#EnableTransactionManagement
public class JbpmDataConfiguration {
#Bean(name = "jbpmDataSource")
#ConfigurationProperties(prefix = "jbpm.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jbpmEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean jbpmEntityManagerFactory(EntityManagerFactoryBuilder builder, #Qualifier("jbpmDataSource") DataSource dataSource) {
Map<String, String> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "create");
return builder.dataSource(dataSource)
.mappingResources("META-INF/Taskorm.xml")
.packages("org.jbpm.services.task.impl.model")
.persistenceUnit("jbpm-persistence-unit")
.properties(properties)
.build();
}
#Bean(name = "jbpmTransactionManager")
public JpaTransactionManager jbpmTransactionManager(#Qualifier("jbpmEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
return jpaTransactionManager;
}
}
In my resources folder I have a kmodule.xml in resources/META-INF/. All my .bpmn files are in resources/com/mydomain/flow/
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
<kbase name="kbase" packages="com.mydomain.flow" />
</kmodule>
I am then creating a runtime engine using the following:
#Configuration
public class MyRuntimeEngine {
#Autowired
#Qualifier("jbpmEntityManagerFactory")
private EntityManagerFactory entityManagerFactory;
#Autowired
#Qualifier("jbpmTransactionManager")
private JpaTransactionManager transactionManager;
#Bean
public RuntimeManager runtimeManager() {
KieServices kieServices = KieServices.Factory.get();
KieContainer kieContainer = kieServices.getKieClasspathContainer();
KieBase kieBase = kieContainer.getKieBase("kbase");
RuntimeEnvironmentBuilder runtimeEnvironmentBuilder = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultInMemoryBuilder()
.entityManagerFactory(entityManagerFactory)
.addEnvironmentEntry(EnvironmentName.TRANSACTION_MANAGER, transactionManager);
RuntimeEnvironment runtimeEnvironment = runtimeEnvironmentBuilder.knowledgeBase(kieBase).get();
return RuntimeManagerFactory.Factory.get().newSingletonRuntimeManager(runtimeEnvironment);
}
}
I am autowiring the RuntimeManager bean into another configuration class so I can configure global variables and work item handlers. This then exposes a KieSession bean once it's done. To then start a process, I am autowiring the KieSession into one of my controllers and calling startProcess:
kieSession.startProcess(processName, processVariables);
which results in the following error:
javax.persistence.TransactionRequiredException: No local transaction to join
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.doJoinTransaction(ExtendedEntityManagerCreator.java:391) ~[spring-orm-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:333) ~[spring-orm-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at com.sun.proxy.$Proxy160.joinTransaction(Unknown Source) ~[na:na]
at org.jbpm.process.audit.JPAWorkingMemoryDbLogger.joinTransaction(JPAWorkingMemoryDbLogger.java:318) ~[jbpm-audit-7.22.0.Final.jar:7.22.0.Final]
at org.jbpm.process.audit.JPAWorkingMemoryDbLogger.persist(JPAWorkingMemoryDbLogger.java:246) ~[jbpm-audit-7.22.0.Final.jar:7.22.0.Final]
at org.jbpm.process.audit.JPAWorkingMemoryDbLogger.afterVariableChanged(JPAWorkingMemoryDbLogger.java:133) ~[jbpm-audit-7.22.0.Final.jar:7.22.0.Final]
at org.drools.core.event.ProcessEventSupport.fireAfterVariableChanged(ProcessEventSupport.java:155) ~[drools-core-7.22.0.Final.jar:7.22.0.Final]
at org.jbpm.process.instance.context.variable.VariableScopeInstance.setVariable(VariableScopeInstance.java:114) ~[jbpm-flow-7.22.0.Final.jar:7.22.0.Final]
at org.jbpm.process.instance.AbstractProcessInstanceFactory.createProcessInstance(AbstractProcessInstanceFactory.java:59) ~[jbpm-flow-7.22.0.Final.jar:7.22.0.Final]
at org.jbpm.process.instance.ProcessRuntimeImpl.startProcess(ProcessRuntimeImpl.java:260) ~[jbpm-flow-7.22.0.Final.jar:7.22.0.Final]
at org.jbpm.process.instance.ProcessRuntimeImpl.createProcessInstance(ProcessRuntimeImpl.java:242) ~[jbpm-flow-7.22.0.Final.jar:7.22.0.Final]
at org.jbpm.process.instance.ProcessRuntimeImpl.createProcessInstance(ProcessRuntimeImpl.java:200) ~[jbpm-flow-7.22.0.Final.jar:7.22.0.Final]
at org.jbpm.process.instance.ProcessRuntimeImpl.startProcess(ProcessRuntimeImpl.java:190) ~[jbpm-flow-7.22.0.Final.jar:7.22.0.Final]
at org.jbpm.process.instance.ProcessRuntimeImpl.startProcess(ProcessRuntimeImpl.java:185) ~[jbpm-flow-7.22.0.Final.jar:7.22.0.Final]
...
How do I properly configure the transaction manager to work with jBPM?
I am also having same trouble in setting up JBPM 7.x version. Were you able to get JBPM running with JPA transaction manager...from the official documentation it seems that we need external transaction manager implementations like Narayanan or bitronix if JBPM needs to be used in embedded mode
I am getting this error while running my application : org.hibernate.service.UnknownUnwrapTypeException: Cannot unwrap to requested type [javax.sql.DataSource]
My configuration class :
#Configuration
#PropertySource("classpath:META-INF/spring/jdbc.properties")
public class HibernateConfig {
#Autowired
private Environment env;
#Bean(name="dataSource")
public DataSource getDataSource() throws PropertyVetoException{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(env.getProperty("db.driverClass"));
dataSource.setJdbcUrl(env.getProperty("db.jdbcUrl"));
dataSource.setUser(env.getProperty("db.user"));
dataSource.setPassword(env.getProperty("db.password"));
dataSource.setMaxPoolSize(50);
dataSource.setMinPoolSize(5);
dataSource.setMaxConnectionAge(1800);
dataSource.setMaxIdleTime(1800);
dataSource.setAutoCommitOnClose(false);
dataSource.setInitialPoolSize(5);
return dataSource;
}
#Bean(name="sessionFactory")
#Scope("singleton")
public LocalSessionFactoryBean getSessionFactory(DataSource dataSource){
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("db.dialect"));
hibernateProperties.setProperty("hibernate.jdbc.batch_size", "0");
hibernateProperties.setProperty("c3p0.acquire_increment", "1");
localSessionFactoryBean.setHibernateProperties(hibernateProperties);
return localSessionFactoryBean;
}
#Bean(name="transactionManager")
public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory){
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory);
return transactionManager;
}
}
I ran actually into this thread which talks about the same issue. However I am wondering if it is a sort of bug in hibernate5 that has not been fixed yet or does it relate to something else. Thank you in advance.
I've been attempting to turn my JHipster generated application into a multi-tenancy app using this - http://jannatconsulting.com/blog/?p=41 blog post as a base.
I've run into a problem with second level caching. Spring boot appears to correctly detect and set up:
DatabaseConfiguration.java
#Configuration
#EnableConfigurationProperties(JpaProperties.class)
#EnableJpaRepositories(
entityManagerFactoryRef = "masterEntityManager",
transactionManagerRef = "masterTransactionManager",
basePackages = {"com.quadrimular.nts.helium.repository.master"})
#EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
#EnableTransactionManagement
#EnableElasticsearchRepositories("com.quadrimular.nts.helium.repository.search")
public class DatabaseConfiguration {
#Inject
private Environment env;
#Autowired(required = false)
private MetricRegistry metricRegistry;
#Inject
private DataSourceProperties datasourceProperties;
#Inject
private JHipsterProperties jhipsterProperties;
#Inject
private JpaProperties jpaProperties;
#Inject
private DataSource dataSource;
#Bean(destroyMethod = "close")
#ConditionalOnExpression("#{!environment.acceptsProfiles('cloud') && !environment.acceptsProfiles('heroku')}")
public DataSource dataSource(DataSourceProperties dataSourceProperties, JHipsterProperties jHipsterProperties) {
log.debug("Configuring Master Datasource");
if (dataSourceProperties.getUrl() == null) {
log.error("Your database connection pool configuration is incorrect! The application" +
" cannot start. Please check your Spring profile, current profiles are: {}",
Arrays.toString(env.getActiveProfiles()));
throw new ApplicationContextException("Database connection pool is not configured correctly");
}
HikariConfig config = new HikariConfig();
config.setDataSourceClassName(dataSourceProperties.getDriverClassName());
config.addDataSourceProperty("url", dataSourceProperties.getUrl());
if (dataSourceProperties.getUsername() != null) {
config.addDataSourceProperty("user", dataSourceProperties.getUsername());
} else {
config.addDataSourceProperty("user", ""); // HikariCP doesn't allow null user
}
if (dataSourceProperties.getPassword() != null) {
config.addDataSourceProperty("password", dataSourceProperties.getPassword());
} else {
config.addDataSourceProperty("password", ""); // HikariCP doesn't allow null password
}
//MySQL optimizations, see https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration
if ("com.mysql.jdbc.jdbc2.optional.MysqlDataSource".equals(dataSourceProperties.getDriverClassName())) {
config.addDataSourceProperty("cachePrepStmts", jHipsterProperties.getDatasource().isCachePrepStmts());
config.addDataSourceProperty("prepStmtCacheSize", jHipsterProperties.getDatasource().getPrepStmtCacheSize());
config.addDataSourceProperty("prepStmtCacheSqlLimit", jHipsterProperties.getDatasource().getPrepStmtCacheSqlLimit());
}
if (metricRegistry != null) {
config.setMetricRegistry(metricRegistry);
}
return new HikariDataSource(config);
}
#Bean(name = "masterEntityManager")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource(datasourceProperties, jhipsterProperties));
em.setPackagesToScan(new String[]{"com.quadrimular.nts.helium.domain.master"});
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalJpaProperties());
em.setPersistenceUnitName("master");
return em;
}
private Properties additionalJpaProperties() {
Properties properties = new Properties();
for (Map.Entry<String, String> entry : jpaProperties.getHibernateProperties(dataSource).entrySet()) {
properties.setProperty(entry.getKey(), entry.getValue());
}
return properties;
}
#Bean(name = "masterTransactionManager")
public JpaTransactionManager transactionManager(EntityManagerFactory masterEntityManager){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(masterEntityManager);
return transactionManager;
}
}
When spring tries to configure:
MultiTenancyJPAConfiguration.java
#Configuration
#EnableConfigurationProperties(JpaProperties.class)
#EnableJpaRepositories(
entityManagerFactoryRef = "tenantEntityManager",
transactionManagerRef = "tenantTransactionManager",
basePackages = {"com.quadrimular.nts.helium.repository.tenant"})
#EnableTransactionManagement
public class MultiTenancyJpaConfiguration {
#Bean(name = "tenantEntityManager")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
MultiTenantConnectionProvider connectionProvider,
CurrentTenantIdentifierResolver tenantResolver) {
LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean();
emfBean.setDataSource(dataSource);
emfBean.setPackagesToScan("com.quadrimular.nts.helium.domain.tenant");
emfBean.setJpaVendorAdapter(jpaVendorAdapter());
Map<String, Object> properties = new HashMap<>();
properties.put(org.hibernate.cfg.Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
properties.put(org.hibernate.cfg.Environment.MULTI_TENANT_CONNECTION_PROVIDER, connectionProvider);
properties.put(org.hibernate.cfg.Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantResolver);
properties.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
emfBean.setJpaPropertyMap(properties);
return emfBean;
}
#Bean(name = "tenantTransactionManager")
public JpaTransactionManager transactionManager(EntityManagerFactory tenantEntityManager){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(tenantEntityManager);
return transactionManager;
}
}
I'm getting this in my trace:
Caused by: org.hibernate.cache.NoCacheRegionFactoryAvailableException: Second-level cache is used in the application, but property hibernate.cache.region.factory_class is not given; please either disable second level cache or set correct region factory using the hibernate.cache.region.factory_class setting and make sure the second level cache provider (hibernate-infinispan, e.g.) is available on the classpath.
I have all the required properties defined in my application-dev.yml
hibernate.cache.use_second_level_cache: true
hibernate.cache.use_query_cache: false
hibernate.generate_statistics: true
hibernate.cache.region.factory_class: org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
It appears that it's being read and used correctly by spring boot for my DatabaseConfiguration.java. I can't work out why it's not detecting the property file. If I try to disable the cache by setting:
hibernate.cache.use_second_level_cache: false
DatabaseConfiguration.java detects and behaves accordingly however MultiTenancyJPAConfiguration.java still throws the same exception.
Am I missing something obvious?
The answer is to actually set the jpa property values on the entity manager. I'm not sure how I overlooked this; I thought that somehow they were already set.
Firstly I injected the main datasource and the jpa properties object provided by spring boot if I'm not mistaken.
MultiTenancyJPAConfiguration.java
#Inject
private JpaProperties jpaProperties;
#Inject
private DataSource dataSource;
I then set the values using the same method used in DatabaseConfiguration.java
MultiTenancyJPAConfiguration.java
#Bean(name = "tenantEntityManager")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
MultiTenantConnectionProvider connectionProvider,
CurrentTenantIdentifierResolver tenantResolver) {
LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean();
emfBean.setDataSource(dataSource);
emfBean.setPackagesToScan("com.quadrimular.nts.helium.domain.tenant");
emfBean.setJpaVendorAdapter(jpaVendorAdapter());
Map<String, Object> properties = new HashMap<>();
properties.put(org.hibernate.cfg.Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
properties.put(org.hibernate.cfg.Environment.MULTI_TENANT_CONNECTION_PROVIDER, connectionProvider);
properties.put(org.hibernate.cfg.Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantResolver);
properties.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
emfBean.setJpaPropertyMap(properties);
emfBean.setJpaProperties(additionalJpaProperties());
return emfBean;
}
private Properties additionalJpaProperties() {
Properties properties = new Properties();
for (Map.Entry<String, String> entry : jpaProperties.getHibernateProperties(dataSource).entrySet()) {
properties.setProperty(entry.getKey(), entry.getValue());
}
return properties;
}
Using the method additionalJpaProperties() to get all the hibernate jpa properties for my main datasource. I then set the hibernate property map properties after the hard coded ones. Clearly not the cleanest solution I plan to set all jpa values from the .yml file.
Well, I'm using spring web MVC for my protect and have two dispatchers configured in my WebAppInitializer:
#Override
public void onStartup(ServletContext container) {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(MvcConfig.class);
rootContext.register(ApplicationContextProvider.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher
= container.addServlet("dispatcher", new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
// Create servlet for WS
MessageDispatcherServlet messageDispatcherServlet = new MessageDispatcherServlet();
messageDispatcherServlet.setApplicationContext(rootContext);
messageDispatcherServlet.setTransformWsdlLocations(true);
ServletRegistration.Dynamic messageDispatcher
= container.addServlet("messageDispatcher", messageDispatcherServlet);
messageDispatcher.setLoadOnStartup(2);
messageDispatcher.addMapping("/ws/*");
One for regular web application, another for soap web service.
#EnableTransactionManagement(proxyTargetClass = true) annotation is set,
I have HibernateTransactionManager bean and SessionFactory configured;
Session factory is used to access db - through DAO, annotated with #Transactional and #Repository;
And well in most cases everything works like a charm, even in the new threads produced by Spring's ThreadPoolTaskExecutor;
However when I try to use my DAO Repository inside of the WS Interceptor - SessionFactory.getCurrentSession fails with:
org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread;
Currently the problem might be solved with following:
Session session;
try {
session = sessionFactory.getCurrentSession();
} catch (HibernateException e) {
session = sessionFactory.openSession();
}
But telling the truth I do not like this solution.
Here is my hibernate properties:
hibernate.format_sql = false
hibernate.show_sql = false
hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
hibernate.hbm2ddl.auto = update
javax.persistence.validation.mode = none
WS interceptor config:
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
PayloadRootSmartSoapEndpointInterceptor interceptor = new PayloadRootSmartSoapEndpointInterceptor(
authEndpointInterceptor, LeadServiceEndpoint.NAMESPACE_URI, "");
interceptors.add(interceptor);
super.addInterceptors(interceptors);
}
So the question is - why there is no session in my session factory bean, when it is accessed from Spring WS interceptor?
Any help is greatly appreciated;
Adding more details:
Interceptor:
#Component
public class AuthEndpointInterceptor implements EndpointInterceptor {
#Autowired
private AuthProvider defaultAuthProvider;
#Override
public boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
// TODO Make marshaller as a bean
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan("com.itsupportme.gpipeline.component.soap.ws.types");
SaajSoapMessage saajSoapMessage = (SaajSoapMessage) messageContext.getRequest();
SoapBody requestBody = saajSoapMessage.getSoapBody();
Object obj = marshaller.unmarshal(requestBody.getPayloadSource());
if (!(obj instanceof KeySignatureAware)) {
// TODO Implement bad response functionality
return false;
}
AuthResultInterface authResult = defaultAuthProvider.doAuth((KeySignatureAware) obj);
if (!authResult.getStatus()) {
// TODO Implement bad response functionality
return false;
}
return true;
}
Provider:
#Service
#Transactional
public class DefaultAuthProvider implements AuthProvider {
#Autowired
private CredentialsDao credentialsDao;
#Override
public AuthResultInterface doAuth(KeySignatureAware keySignatureAware) {
// Check key and signature
if (keySignatureAware.getKey() == null) {
return new AuthResult(false, "Key is not provided.");
}
if (keySignatureAware.getSignature() == null) {
return new AuthResult(false, "Signature is not provided.");
}
// Find if such credentials are there
Credentials credentials = credentialsDao.findByKey(keySignatureAware.getKey());
if (credentials == null) {
return new AuthResult(false, "Invalid key provided");
}
...
}
DAO:
#Repository
#Transactional
public class CredentialsDaoImpl implements CredentialsDao{
#Autowired
SessionFactory sessionFactory;
#Override
#SuppressWarnings("unchecked")
public Credentials findByKey(String key) {
List<Credentials> credentialsList;
Session session = sessionFactory.getCurrentSession();;
credentialsList = session
.createQuery("from Credentials where apiKey = :apiKey")
.setParameter("apiKey", key)
.list();
if (credentialsList.size() > 0){
return credentialsList.get(0);
} else {
return null;
}
}
}
Problem was solved by marking #Repository bean - CredentialsDaoImpl as PROTOTYPE_SCOPE bean. And retrieving repo. instance from application context;
As result spring creates new repository bean for thread in which repo. is called and (from my understanding) creates an appropriate session associated with this thread;
I'm having this error while trying to connect to mySQL database:
No object bound to name java:comp/env/jdbc/mySql
This is my configuration class:
#Configuration
#EnableTransactionManagement
#ComponentScan({ "org.onmyown.config" })
#PropertySource(value = { "classpath:application.properties" })
public class HibernateConfiguration {
#Autowired
private Environment environment;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "org.onmyown" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource dataSource() {
DataSource dataSource = null;
try {
Context initialContext = new InitialContext();
Context environmentContext = (Context)initialContext.lookup("java:comp/env");
dataSource = (DataSource) environmentContext.lookup("jdbc/mySql");
} catch (NamingException e) {
e.printStackTrace();
}
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
properties.put("hibernate.hbm2ddl.auto", environment.getRequiredProperty("hibernate.hbm2ddl.auto"));
return properties;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
I can ping from GlassFish just fine. I put all the properties I need (I think). My friend did this configuration with Derby database and it works. Is there any difference?
#edit
I 'fixed' something. I added web.xml, everything is here:
https://github.com/afterlook/SpringMVC
The problem now is that the application doesn't really care that i put mySql as a data source. It searches for DerbyPool which is one of the defaults in GlassFish. Any idea why?
The problem you posted originaly tries to use a datasource that is looked up using JNDI with the key comp/env/jdbc/mySql and this is not configured.
If you want to use a connection pool of Glasfish, you have to configure Glasfish so it creates that pool and publishes it as a JNDI component with the key comp/env/jdbc/mySql
mysql glasfish shows how this is done.
With your fix you commented the JNDI code out, instead you set the connection url from a property.
db.url=jdbc:mysql://localhost:3306/app
without additional configuration, this seems to point to the Glasfish default connection pool using the derby db.
If you want to use the Glasfish connection pool :
Configure Glasfish to crate that pool using mysql as described in the linked document.
You will configure url,user,password for the Glsfish connection pool, so I think you dont have to provide it in the spring config again. Take the datasource as you get it from the JNDI lookup.
Revert your fix that commented out the JNDI lookup.
Your code should look like this :
#Bean
public DataSource dataSource() {
DataSource dataSource = null;
try {
Context initialContext = new InitialContext();
Context environmentContext = (Context)initialContext.lookup("java:comp/env");
dataSource = (DataSource) environmentContext.lookup("jdbc/mySql");
} catch (NamingException e) {
e.printStackTrace();
}*/
return dataSource;
}