I am using Spring 3.1 and Hibernate 3.0 with the below configuration to test the Declarative Transactions, but am not able to see the roll back statements.
Here i have noticed, every time it is opening a new session. I guess this may cause the issue.
Is there anything wrong in my config / code..?
{
Log
----
09:40:54.909 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0'
09:40:54.914 [main] DEBUG o.s.t.i.NameMatchTransactionAttributeSource - Adding transactional method [save*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-SNTransException]
09:40:54.914 [main] DEBUG o.s.t.i.NameMatchTransactionAttributeSource - Adding transactional method [get*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly]
09:40:54.914 [main] DEBUG o.s.t.i.NameMatchTransactionAttributeSource - Adding transactional method [*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
09:40:54.936 [main] DEBUG o.s.o.hibernate3.SessionFactoryUtils - Opening Hibernate Session
09:40:54.960 [main] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13750710549
09:40:54.978 [main] DEBUG o.h.e.def.AbstractSaveEventListener - executing identity-insert immediately
09:40:54.979 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
09:40:54.979 [main] DEBUG org.hibernate.jdbc.ConnectionManager - opening JDBC connection
09:40:54.979 [main] DEBUG o.s.j.d.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/test]
09:40:54.992 [main] DEBUG org.hibernate.SQL - insert into springtrans.users (username, password, status) values (?, ?, ?)
Hibernate: insert into springtrans.users (username, password, status) values (?, ?, ?)
09:40:55.009 [main] DEBUG o.h.id.IdentifierGeneratorFactory - Natively generated identity: 458
09:40:55.009 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
09:40:55.013 [main] DEBUG o.s.orm.hibernate3.HibernateTemplate - Eagerly flushing Hibernate session
09:40:55.014 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - processing flush-time cascades
09:40:55.014 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - dirty checking collections
09:40:55.018 [main] DEBUG org.hibernate.engine.Collections - Collection found: [com.snsystems.data.Users.userSettingses#458], was: [<unreferenced>] (initialized)
09:40:55.031 [main] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13750710550
09:40:55.032 [main] DEBUG o.h.e.def.AbstractSaveEventListener - executing identity-insert immediately
09:40:55.032 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
09:40:55.032 [main] DEBUG org.hibernate.jdbc.ConnectionManager - opening JDBC connection
09:40:55.032 [main] DEBUG o.s.j.d.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/test]
09:40:55.047 [main] DEBUG org.hibernate.SQL - insert into springtrans.user_settings (user_id, last_login_ip, failed_logins) values (?, ?, ?)
Hibernate: insert into springtrans.user_settings (user_id, last_login_ip, failed_logins) values (?, ?, ?)
09:40:55.055 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
09:40:55.061 [main] DEBUG o.h.util.JDBCExceptionReporter - could not insert: [com.snsystems.data.UserSettings] [insert into springtrans.user_settings (user_id, last_login_ip, failed_logins) values (?, ?, ?)]
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'last_login_ip' cannot be null
}
applicationContext.xml
{
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location"><value>/jdbc.properties</value></property>
</bean>
<!-- Data Source -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>${jdbc.driverClassName}</value></property>
<property name="url"><value>${jdbc.url}</value></property>
<property name="username"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
</bean>
<!-- Spring hibernate integration -->
<!-- Transaction Manager -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- Transaction Template Bean -->
<bean id="transactionTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref local="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="view*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>/com/snsystems/data/Users.hbm.xml</value>
<value>/com/snsystems/data/UserSettings.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.generate_statistics">true</prop>
</props>
</property>
</bean>
<bean id="usersDao" class="com.snsystems.dao.impl.UsersDaoImpl">
<property name="hibernateTemplate" ref="hibernateTemplate" />
</bean>
<bean id="userSettingsDao" class="com.snsystems.dao.impl.UserSettingsDaoImpl">
<property name="hibernateTemplate" ref="hibernateTemplate" />
</bean>
<bean id="userService" class="com.snsystems.service.impl.UsersServiceImpl">
<property name="usersDao" ref="usersDao" />
<property name="userSettingsDao" ref="userSettingsDao" />
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" rollback-for="SNTransException"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="userServiceOperation" expression="execution(* com.snsystems.service..Service.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="userServiceOperation"/>
</aop:config>
}
UsersDaoImpl
public class UsersDaoImpl implements IUsersDao {
private HibernateTemplate hibernateTemplate;
public void saveOrUpdateUsers(Users users) throws PersistenceException {
try {
hibernateTemplate.saveOrUpdate(users);
} catch (Exception e) {
throw new PersistenceException(e);
}
}
public void deleteUsers(Users users) throws PersistenceException {
try {
hibernateTemplate.delete(users);
} catch (Exception e) {
throw new PersistenceException(e);
}
}
public HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
}
UsersServiceImpl
public class UsersServiceImpl implements IUsersService
{
private IUsersDao usersDao = null;
private IUserSettingsDao userSettingsDao = null;
public void saveOrUpdateUsers(Users users) throws SNTransException {
try {
usersDao.saveOrUpdateUsers(users);
} catch (PersistenceException e) {
throw new SNTransException(e);
}
}
public void deleteUsers(Users users) throws SNTransException {
try {
usersDao.deleteUsers(users);
} catch (PersistenceException e) {
throw new SNTransException(e);
}
}
public void saveOrUpdateUserSettings(UserSettings userSettings) throws SNTransException {
try {
userSettingsDao.saveOrUpdateUserSettings(userSettings);
} catch (PersistenceException e) {
throw new RuntimeException(e);
}
}
public void deleteUserSettings(UserSettings userSettings) throws SNTransException {
try {
userSettingsDao.deleteUserSettings(userSettings);
} catch (PersistenceException e) {
throw new SNTransException(e);
}
}
.. setters and getters for dao's
}
UsersServiceTest
{
public class UsersServiceTest {
private static ClassPathXmlApplicationContext context = null;
private static IUsersService usersService = null;
private static Users users = null;
private static UserSettings userSettings = null;
#Before
public void setUp() {
try {
context = new ClassPathXmlApplicationContext("applicationContextBack.xml");
usersService = (IUsersService) context.getBean("userService");
} catch (Exception e) {
e.printStackTrace();
}
}
#After
public void tearDown() throws Exception {
usersService = null;
context = null;
}
#Test
public void testSaveOrUpdateUserSettings() throws Exception {
users = new Users();
users.setUsername("JUnitTest");
users.setPassword("JUnitTest");
users.setStatus("inactive");
usersService.saveOrUpdateUsers(users);
assertNotNull("User Id is null", users.getId());
userSettings = new UserSettings();
userSettings.setUsers(users);
usersService.saveOrUpdateUserSettings(userSettings);
assertNotNull("UserSettings Id is null", userSettings.getId());
}
}
}
You should add #Transactional annotation to your service (impl) methods.
Example:
#Transactional
public void saveOrUpdateUsers(Users users) {
...
}
so you want spring transaction manager to roll back transaction after each test? then the Spring TestContext Framework may help
add annotation to your test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("applicationContext.xml")
#Transactional
public class UsersServiceTest {
}
spring transaction manager will automatically start a new transaction before test and rollback it when test finishes
Related
I configured Spring Aop and Transaction.
When I run the Project Test. the Transaction does not work and i got the log output as follows: "was not registered for synchronization because synchronization is not active"
When I run LoginServiceImpl.findMemberInfo() method, I got the Transaction registered Successfully. However, I run LoginServiceImpl.insertMember, the registration fails
root-context.xml as shown below:
<context:component-scan base-package:"com.quapenus">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<!-- transactionManager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- txAdvice -->
<tx:annotation-driven mode="aspectj" />
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="find*" />
<tx:method name="insert*" />
</tx:attributes>
</tx:advice>
<!-- Aspect -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.quapenus.*.service.*Impl.*(..))" />
<aop:advisor pointcut-ref="pointcut" advice-ref="txAdvice" />
</aop:config>
servlet-context as alown below:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<bean id="jsonMapper" class="com.fasterxml.jackson.databind.ObjectMapper" />
<bean id="jsonView" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
<property name="contentType" value="application/json;charset=UTF-8" />
<property name="objectMapper" ref="jsonMapper" />
</bean>
LoginServiceImpl as shwon below:
#Service
public class LoginServiceImpl implements LoginService {
Logger logger = LoggerFactory.getLogger(LoginServiceImpl.class);
#Autowired
private LoginDao loginDao;
#Override
public Member findMemberInfo(String username) throws UsernameNotFoundException {
Member member = loginDao.findMemberInfo(username);
if(member == null){
throw new UsernameNotFoundException("아이디가 존재하지 않습니다.");
}
return member;
}
#Override
public List<GrantedAuthority> findRoles(String username) throws NotFoundException {
List<GrantedAuthority> roles = loginDao.findRoles(username);
if(roles.size() == 0){
throw new NotFoundException("해당 역할이 없습니다.");
}
return roles;
}
#Override
public void insertMember(Member member) throws RollbackException {
int result = loginDao.insertMember(member);
if(result == 1){
throw new RollbackException("Test Tx");
}
}
}
LoginDao as shown below:
#Repository
public class LoginDao {
#Autowired
private SqlSession sqlSession;
public Member findMemberInfo(String username) {
return sqlSession.selectOne("com.quapenus.common.sql.LoginMapper.findMemberInfo", username);
}
public List<GrantedAuthority> findRoles(String username) {
return sqlSession.selectList("com.quapenus.common.sql.LoginMapper.findRoles", username);
}
public int insertMember(Member member) {
return sqlSession.insert("com.quapenus.common.sql.LoginMapper.insertMember", member);
}
}
LoginServiceImpl.insertMember() Method log as shown below:
org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession#77b976a0] was not registered for synchronization because synchronization is not active
17:13:01.100 [http-nio-8080-exec-6] DEBUG o.s.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
LoginServiceImpl.findMemberInfo() Method log as shown below:
org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
17:58:07.052 [http-nio-8080-exec-9] DEBUG org.mybatis.spring.SqlSessionUtils - Registering transaction synchronization for SqlSession
github repository: https://github.com/Sujin1135/Quapenus
I'm trying to get Javer setup with my project. I am using Hibernate JPA and I believe I have everything configured correctly.
Here is my Spring config:
<bean id="auditDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.audit.url}"/>
<property name="username" value="${jdbc.audit.username}"/>
<property name="password" value="${jdbc.audit.password}"/>
<property name="maxActive" value="100"/>
<property name="maxWait" value="1000"/>
<property name="poolPreparedStatements" value="true"/>
<property name="defaultAutoCommit" value="true"/>
<property name="validationQuery" value="${jdbc.validationQuery}"/>
<property name="testOnBorrow" value="true"/>
</bean>
<bean id="auditSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" destroy-method="destroy">
<property name="dataSource" ref="auditDataSource"/>
<property name="configLocation" value="classpath:hibernate.audit.cfg.xml"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.query.substitutions">true 'Y', false 'N'</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">false</prop>
<prop key="hibernate.use_sql_comments">false</prop>
<!--<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>-->
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop>
<!-- Hibernate Search index directory -->
<prop key="hibernate.search.default.indexBase">${app.search.index.basedir}</prop>
</props>
<!-- Turn batching off for better error messages under PostgreSQL -->
<!-- hibernate.jdbc.batch_size=0 -->
</property>
</bean>
<!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
<bean id="auditTransactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="auditSessionFactory"/>
</bean>
<bean id="javersRepoConnectionProvider" class="com.dsc.discus.ng.audit.javers.JaversRepoConnectionProvider">
<constructor-arg name="dataSource" ref="auditDataSource"/>
</bean>
<bean id="auditController" class="com.dsc.discus.ng.audit.javers.JaversAuditController">
<constructor-arg name="javersRepoConnectionProvider" ref="javersRepoConnectionProvider"/>
<constructor-arg name="transactionManager" ref="auditTransactionManager"/>
</bean>
The JaversAuditController bean:
public class JaversAuditController extends AuditController {
private final JaversSqlRepository javersSqlRepository;
private final Javers javers;
public JaversAuditController(JaversRepoConnectionProvider javersRepoConnectionProvider, PlatformTransactionManager transactionManager) {
javersSqlRepository = SqlRepositoryBuilder.sqlRepository()
.withConnectionProvider(javersRepoConnectionProvider)
.withDialect(DialectName.MYSQL)
.build();
javers = TransactionalJaversBuilder.javers()
.withTxManager(transactionManager)
.withObjectAccessHook(new HibernateUnproxyObjectAccessHook())
.withMappingStyle(MappingStyle.BEAN)
.registerJaversRepository(javersSqlRepository)
.build();
}
#Override
public void addModified(BaseEntity entity) {
addModified(entity, null);
}
#Override
public void addModified(BaseEntity entity, Map<String, String> extraParams) {
if (javers != null && entity != null) {
if (extraParams != null) {
javers.commit(entity.getModifiedBy(), entity, extraParams);
}
else {
javers.commit(entity.getModifiedBy(), entity);
}
}
}
public JaversSqlRepository getJaversSqlRepository() {
return javersSqlRepository;
}
public Javers getJavers() {
return javers;
}
I have to use BEAN mapping style because the JPA annotations are on the accessors of the entities.
Here is the JaversRepoConnectionProvider bean:
public class JaversRepoConnectionProvider implements ConnectionProvider {
private DataSource dataSource;
public JaversRepoConnectionProvider(DataSource dataSource) {
this.dataSource = dataSource;
}
#Override
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
I am doing a manual audit. That is, every time an entity is saved, I'm calling the javers.commit() method. As it happens, the very first thing I try to save causes a MySQL syntax error stating that the table "jv_commit" doesn't exist. The documentation states that the necessary tables would automatically be created, so I must be missing something. Please advise and thanks in advance.
Truncated stack trace:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'discus_eng_audit.jv_commit' doesn't exist
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
at com.mysql.jdbc.Util.getInstance(Util.java:386)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1054)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4237)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4169)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2617)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2778)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2825)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2156)
at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2323)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
at org.polyjdbc.core.transaction.Transaction.executeQuery(Transaction.java:59)
at org.polyjdbc.core.query.TransactionalQueryRunner.queryCollection(TransactionalQueryRunner.java:86)
at org.polyjdbc.core.query.TransactionalQueryRunner.queryList(TransactionalQueryRunner.java:76)
at org.javers.repository.sql.PolyUtil.queryForBigDecimalList(PolyUtil.java:27)
at org.javers.repository.sql.PolyUtil.queryForOptionalBigDecimal(PolyUtil.java:36)
at org.javers.repository.sql.repositories.CommitMetadataRepository.selectMaxCommitId(CommitMetadataRepository.java:86)
at org.javers.repository.sql.repositories.CommitMetadataRepository.getCommitHeadId(CommitMetadataRepository.java:75)
at org.javers.repository.sql.JaversSqlRepository.getHeadId(JaversSqlRepository.java:76)
at org.javers.repository.api.JaversExtendedRepository.getHeadId(JaversExtendedRepository.java:137)
at org.javers.core.commit.CommitIdFactory.nextId(CommitIdFactory.java:26)
at org.javers.core.commit.CommitFactory.newCommitMetadata(CommitFactory.java:79)
at org.javers.core.commit.CommitFactory.create(CommitFactory.java:69)
at org.javers.core.JaversCore.commit(JaversCore.java:82)
at org.javers.core.JaversCore.commit(JaversCore.java:67)
at org.javers.spring.jpa.JaversTransactionalDecorator.commit(JaversTransactionalDecorator.java:58)
at com.dsc.discus.ng.audit.javers.JaversAuditController.addModified(JaversAuditController.java:56)
at com.dsc.discus.ng.audit.javers.JaversAuditController.addModified(JaversAuditController.java:46)
at com.dsc.discus.ng.model.persistence.dao.hibernate.BaseDaoHibernate.save(BaseDaoHibernate.java:146)
at com.dsc.discus.ng.model.persistence.dao.hibernate.BaseDaoHibernate.save(BaseDaoHibernate.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
In Spring app, Javers instance should be registered as a Spring bean. JaVers uses #PostConstruct annotation and Spring calls ensureSchema() when txManager is ready.
See JaversTransactionalDecorator impl:
#PostConstruct
public void ensureSchema() {
TransactionTemplate tmpl = new TransactionTemplate(txManager);
tmpl.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
javersSqlRepository.ensureSchema();
}
});
}
In your case, this method isn't called because you have created the Javers instance in your Controller and not as a Spring bean. You should configure your app as docs says - https://javers.org/documentation/spring-integration/#jpa-entity-manager-integration
It turns out that the TransactionalJaversBuilder.javers().build() method does not ensure the tables are created, like the standard JaversBuilder.javers().build() method does. I am not sure if this is bug in the TransactionalJaversBuilder or not. My workaround is to call the ensureSchema() after the Javers object is built:
public JaversAuditController(JaversRepoConnectionProvider javersRepoConnectionProvider, PlatformTransactionManager transactionManager) {
javersSqlRepository = SqlRepositoryBuilder.sqlRepository()
.withConnectionProvider(javersRepoConnectionProvider)
.withDialect(DialectName.MYSQL)
.build();
javers = TransactionalJaversBuilder.javers()
.withTxManager(transactionManager)
.withObjectAccessHook(new HibernateUnproxyObjectAccessHook())
.withMappingStyle(MappingStyle.BEAN)
.registerJaversRepository(javersSqlRepository)
.build();
javersSqlRepository.ensureSchema();
}
I added aspect advice to lazy loading for init session.
After migrating to Spring 4, Hibernate 4, Java 8 got exception:
org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
Hibernate config:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="reattachSessionAspect" class="prj.ReattachSessionAspect" factory-method="aspectOf"/>
<bean class="org.springframework.transaction.aspectj.AnnotationTransactionAspect" factory-method="aspectOf"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan">
<list>
// model packages
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL82Dialect</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.internal.NoCachingRegionFactory</prop>
<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.connection.autocommit">true</prop>
<prop key="hibernate.connection.release_mode">after_statement</prop>
<prop key="hibernate.connection.pool_size">0</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
Aspect class (exception caused by sessionFactory.getCurrentSession()):
#Aspect
public class Aspect {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
private static ThreadLocal<Boolean> LOCK = new ThreadLocal<Boolean>();
#Around("reattachSession()")
public Object reattachSession(ProceedingJoinPoint pjp) throws Throwable {
Object obj = pjp.proceed();
if (obj instanceof AbstractPersistentCollection) {
AbstractPersistentCollection ps = (AbstractPersistentCollection) obj;
Field field = getClass(ps.getClass()).getDeclaredField("session");
field.setAccessible(true);
SessionImpl o = (SessionImpl)field.get(ps);
if (!ps.wasInitialized() && (o == null || o.isClosed()) && (LOCK.get()==null || !LOCK.get())) {
LOCK.set(true);
Transaction t = null;
try {
Session session = sessionFactory.getCurrentSession();
t = session.beginTransaction();
session.update(pjp.getTarget());
Hibernate.initialize(ps);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
if (t != null && t.isActive()) {
t.rollback();
}
throw new RuntimeException(e);
} finally {
LOCK.set(false);
}
}
}
return obj;
}
private Class getClass(Class clazz) {
if(clazz.equals(AbstractPersistentCollection.class)){
return clazz;
} else {
return getClass(clazz.getSuperclass());
}
}
#Pointcut("execution(#prj.AddSession * *(..))")
public void reattachSession(){
}
}
Method that cause exception when called:
#Lazy
#Transactional(propagation = Propagation.REQUIRED)
public List<Detail> getDetails() {
return details;
}
In services layers session getting right. And before migrating all worked. Before migrating i had current_session_context_class=thread.
Can someone advise me what i doing wrong ?
I've read tens of topics and no result.
What the problem:
I can't get current hibernate session in #Transactional method.
EXCEPTION:
Exception in thread "main" org.hibernate.HibernateException: No Session found for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:988)
at name.krestjaninoff.activiti.hello.core.Engine.getHibernateSession(Engine.java:27)
at name.krestjaninoff.activiti.hello.process.CreateClientService.execute(CreateClientService.java:42)
at org.activiti.engine.impl.bpmn.JavaDelegateDelegate.execute(JavaDelegateDelegate.java:48)
at org.activiti.engine.impl.bpmn.JavaDelegateDelegate.execute(JavaDelegateDelegate.java:39)
at org.activiti.engine.impl.bpmn.ClassDelegate.execute(ClassDelegate.java:96)
at org.activiti.engine.impl.pvm.runtime.AtomicOperationActivityExecute.execute(AtomicOperationActivityExecute.java:40)
....
My Code:
#Service
#EnableTransactionManagement(proxyTargetClass=true, mode= AdviceMode.PROXY)
public class CreateClientService {
#Transactional(value="transactionManager", propagation = Propagation.REQUIRED)
public void execute() {
((SessionFactory) applicationContext.getBean("sessionFactory")).getCurrentSession(); //EXCEPTOIN!!!
}
}
My configurations:
<tx:annotation-driven/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" scope="singleton">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan">
<list>
<value>name.krestjaninoff.activiti.hello.db</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL82Dialect</prop>
<!--<prop key="hibernate.hbm2ddl.auto">create</prop>-->
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager" scope="singleton">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
<property name="dataSource">
<ref local="dataSource" />
</property>
</bean>
ADDTION: NativeConstructorAccessorImpl class create CreateClientService instance:
#CallerSensitive
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
return (T) ca.newInstance(initargs);
}ected JavaDelegate javaDelegate;
protected JavaDelegateDelegate() {
}
public JavaDelegateDelegate(JavaDelegate javaDelegate) {
this.javaDelegate = javaDelegate;
}
public void execute(ActivityExecution execution) throws Exception {
execute((DelegateExecution) execution);
performDefaultOutgoingBehavior(execution);
}
public void notify(ExecutionListenerExecution execution) throws Exception {
execute((DelegateExecution) execution);
}
public void execute(DelegateExecution execution) throws Exception {
javaDelegate.execute(execution);
}
}
Hi I'm trying a little POC with JPA and unit test to verify that the DB schema is created. I'm working with H2 DB and I set to Hibernate create the schema from the entities, but when DbUnit tries to initialize the DB from a dataset I always get a Table ... not found in tableMap. I read that I have to add the property DB_CLOSE_DELAY=-1 to DB URL but is like after Hibernate creates the schema the DB is losted when DbUnit tries to initialize.
Any ideas? Any help is highly appreciated.
This is my config:
application-context.xml
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSourceH2" />
<property name="packagesToScan" value="com.xxx.model" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="true" />
<property name="showSql" value="true" />
<!-- property name="databasePlatform" value="org.hibernate.dialect.MySQLInnoDBDialect" /-->
<property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
<!-- property name="database" value="MYSQL" /-->
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="javax.persistence.validation.mode">CALLBACK</prop>
</props>
</property>
</bean>
<bean id="dataSourceH2"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
RepositoryTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/application-context-test.xml" })
#Transactional
public class SystemEntityRepositoryH2Test {
#Inject
private SystemEntityRepository repository;
#Inject
private DataSource dataSourceH2;
private IDatabaseConnection connection;
#Before
public void setUp() throws Exception {
IDatabaseConnection dbUnitCon = null;
dbUnitCon = new DatabaseDataSourceConnection(dataSourceH2, "testdb");
dbUnitCon.getConfig().setProperty(DatabaseConfig.FEATURE_QUALIFIED_TABLE_NAMES, true);
IDataSet dataSet = this.getDataSet("dataset-systementity.xml");
DatabaseOperation.INSERT.execute(dbUnitCon, dataSet);
}
#After
public void tearDown() throws Exception {
//DatabaseOperation.DELETE_ALL.execute(this.getConnection(), this.getDataSet(dataSetFile));
}
#Test
public void test() throws Exception {
}
protected IDataSet getDataSet(String dataSetFile) throws Exception {
ResourceLoader resourceLoader = new ClassRelativeResourceLoader(this.getClass());
Resource resource = resourceLoader.getResource(dataSetFile);
if (resource.exists()) {
return new FlatXmlDataSetBuilder().build(resource.getInputStream());
}
return null;
}
}
dataset-systementity.xml
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<System_Entities id="2" name="NAME" phone01="+52-55-55555555" email="a#a.com"
address01="Street" address02="123" address03="1" address04="Address04"
address05="Address05" city="City" state="State" country="MX"
zipcode="12345" />
</dataset>
Error
ERROR DatabaseDataSet:286 - Table 'System_Entities' not found in tableMap=org.dbunit.dataset.OrderedTableNameMap[_tableNames=[], _tableMap={}, _caseSensitiveTableNames=false]
I can see that the tables are created by hibernate because the log shows all the sql sentences without error.
Thanks.
SOLUTION
Thanks Mark Robinson
I modified the setUp method to:
#Before
public void setUp() throws Exception {
IDatabaseConnection dbUnitCon = null;
EntityManager entityManager = entityManagerFactory.createEntityManager();
Session session = entityManager.unwrap(Session.class);
SessionImplementor si = (SessionImplementor) session;
Connection conn = si.getJdbcConnectionAccess().obtainConnection();
dbUnitCon = new DatabaseConnection(conn);
//dbUnitCon.getConfig().setProperty(DatabaseConfig.FEATURE_QUALIFIED_TABLE_NAMES, true);
IDataSet dataSet = this.getDataSet("dataset-systementity.xml");
DatabaseOperation.INSERT.execute(dbUnitCon, dataSet);
}
It works now, what I don't understand yet is if I use HSQLDB I don't have this problem.
The problem is that DBUnit is loading the table data before Hibernate can initialize.
As part of your #setup, you'll need to get the Hibernate session. This should cause Hibernate to create your table. You could even force it by executing a simple query like select 1