Reputation: 567
I am trying to user hibernate with spring transaction support.I've configured session factory as bean(see below) and made a DAO class.I am also using spring test support -- running the test class with @Transactional annotation,which(as i understand) should allow me to call multiple dao operations in a single transaction.I read that hibernate should flush its persistence context either after commit or before a select is executed.I had my hopes on the latter.But most of my tests failed because(according to the logs) hibernate didn't flush it's persistence context after insert and before select.
Here is the spring part of the spring configuration:
@Configuration
@PropertySource("/db.properties")
@EnableTransactionManagement
@ComponentScan(basePackages = "repository")
public class DataAccessConfiguration {
@Bean
public SessionFactory sessionFactory() throws IOException {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(pool());
sessionFactoryBean.setPackagesToScan("domain");
sessionFactoryBean.setHibernateProperties(hibernateProperties());
sessionFactoryBean.afterPropertiesSet();
return sessionFactoryBean.getObject();
}
private Properties hibernateProperties() {
Properties hibernateProp = new Properties();
hibernateProp.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
hibernateProp.put("hibernate.format_sql", true);
hibernateProp.put("hibernate.use_sql_comments", true);
hibernateProp.put("hibernate.show_sql", true);
hibernateProp.put("hibernate.max_fetch_depth", 3);
hibernateProp.put("hibernate.jdbc.batch_size", 10);
hibernateProp.put("hibernate.jdbc.fetch_size", 50);
hibernateProp.put("hibernate.hbm2ddl.auto", "update");
return hibernateProp;
}
@Bean
@Profile("hibernate")
public PlatformTransactionManager transactionManager() throws IOException {
HibernateTransactionManager manager = new HibernateTransactionManager(sessionFactory());
manager.setDataSource(pool());
return manager;
}
@Bean
@Profile("jdbc")
PlatformTransactionManager jdbcTransactionManager(){
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(pool());
return manager;
}
}
I am using two platform transaction managers -- one for jdbc and another for hibernate.I've tried deleting the jdbc manager leaving only hibernate(didn't work).
Here is the dao class:
@Repository
@Profile("hibernate")
public class UserDaoHibernate implements UserDao {
private final SessionFactory sessionFactory;
public UserDaoHibernate(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
public void insert(User user) {
sessionFactory.getCurrentSession().save(user);
}
@Override
public void addRole(User user, Role role) {
NativeQuery addRoleQuery = sessionFactory.getCurrentSession()
.createNativeQuery("INSERT INTO roles(user_name, user_role) VALUES(:user_name,:user_role)");
addRoleQuery.setParameter("user_name",user.getUsername());
addRoleQuery.setParameter("user_role", role.toString().toUpperCase());
addRoleQuery.executeUpdate();
}
@Override
public boolean hasRole(User user, Role role) {
NativeQuery hasRoleQuery = sessionFactory.getCurrentSession()
.createSQLQuery("SELECT * FROM roles WHERE user_name = :user_name AND user_role = :user_role");
hasRoleQuery.setParameter("user_name", user.getUsername());
hasRoleQuery.setParameter("user_role", role.toString().toUpperCase());
return hasRoleQuery.getResultList().size() == 1;
}
}
The test code that fails with the test class is here :
@ContextConfiguration(classes = {DataAccessConfiguration.class})
@Rollback
@ExtendWith(SpringExtension.class)
@ActiveProfiles("hibernate")
@Transactional
public class UserDaoTest {
@Autowired
private UserDao sut;
private User user = new User("NEW_USERNAME123", "NEW_PASSWORD123", LocalDate.now().minusYears(20),
"FIRST_NAME", "LAST_NAME");
@Test
public void shouldAddNewRoleToUser(){
sut.insert(user);
assertFalse(sut.hasRole(user,Role.MEMBER));
sut.addRole(user, Role.MEMBER);
assertTrue(sut.hasRole(user,Role.MEMBER));
}
}
And here is the output i get afte running the test method(transaction comments not included):
Hibernate:
select
nextval ('USER_ID_SEQUENCE')
Hibernate:
/* dynamic native SQL query */ SELECT
*
FROM
roles
WHERE
user_name = ?
AND user_role = ?
Hibernate:
/* dynamic native SQL query */ INSERT
INTO
roles
(user_name, user_role)
VALUES
(?,?)
Nov 13, 2019 3:05:31 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
WARN: SQL Error: 0, SQLState: 23503
Nov 13, 2019 3:05:31 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: ERROR: insert or update on table "roles" violates foreign key constraint "fk_roles_user_id"
Detail: Key (user_name)=(NEW_USERNAME123) is not present in table "users_table".
When i programmatically commit the session with sessionFactory.getCurrentSession().getTransaction().commit(); at end of insert everything works.
Upvotes: 0
Views: 614
Reputation: 567
So far the only thing i found about my issue is here How does AUTO flush strategy works
Apparently the AUTO flush mode doesn't guarantee flush before EVERY query(At least not with hibernate own API).I am not 100% sure if this is my case,i'll try to find out.
Upvotes: 0
Reputation: 132
You should make "shouldAddNewRoleToUser()" method annotated with @Transactional and also check have you configured spring session context in application for transactions.
Upvotes: 0
Reputation: 825
Try to make insert method @Transactional or use sessionFactory.getCurrentSession().flush()
Upvotes: 0