I am upgrading a web application from:
Spring (MVC) 4.2.9.RELEASE, Hibernate 4.3.8.Final, and 1.7.1.RELEASE
to
Spring (MVC) 5.0.2.RELEASE, Hibernate 5.2.12.Final, and Spring Data 2.0.2.RELEASE.
The web application runs on Windows and MS SQL Server 2014 Enterprise.
The upgrade did not force me to change the settings for Hibernate and JPA. However, now the program behaves very differently, which is explained below.
The following is a typcial service method in the application, which saves a new Account object, and then updates its value if it is a new object, then return it. The Account object has an ID field defined as follows:
@Id
@GeneratedValue( strategy = GenerationType.IDENTITY )
private Long id;
This is the servie method:
@Override
@Transactional
public Account saveAccount(Account acc) {
//Step 1
//save method from Spring Data
accountRepository.save(acc);
//stringId is set manually only once and it is set only when a new accout object is created
if (acc.getStringId() == null) {
//Step 2
acc.setStringId("some_string_id");
}
return acc;
}
Here is the behavior BEFORE the upgrade:
Step 1: acc is the same object after the save. Its id field is updated from null to a Long value after the save.
Step 2: The new value is automatically serialized to database. Note that there is no explicit database call to the save method.
Here is the behavior AFTER the upgrade:
Step 1: Using accountRepository.save(acc)
only does not update the acc object. To get the object with new id, I have to do it the following way:
acc = accountRepository.save(acc)
Step 2: the string id is not saved for a new object.
I am looking for ways to make the system work the way before it was upgrade. The reason is that the application is not trivial, and I have followed the programming pattern (good or bad) throught the application. I would like to avoid lots of changes and re-tests.
Here is the related configuration
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd">
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg ref="hikariConfig" />
</bean>
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="poolName" value="derek6HikariCP" />
<property name="connectionTestQuery" value="${jdbc.connectionTestQuery}" />
<property name="dataSourceClassName" value="${jdbc.dataSourceClassName}" />
<property name="maximumPoolSize" value="${jdbc.maximumPoolSize}" />
<property name="minimumIdle" value="${jdbc.minimumIdle}" />
<property name="idleTimeout" value="${jdbc.idleTimeout}" />
<property name="connectionTimeout" value="${jdbc.connectionTimeout}" />
<property name="dataSourceProperties">
<props>
<prop key="url">${jdbc.url}</prop>
<prop key="user">${jdbc.username}</prop>
<prop key="password">${jdbc.password}</prop>
</props>
</property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource"/>
</bean>
<bean id="entityManagerFactory" name="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="packagesToScan" value="myproject.entity" />
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
<prop key="hibernate.max_fetch_depth">${hibernate.max_fetch_depth}</prop>
<prop key="hibernate.jdbc.fetch_size">${hibernate.jdbc.fetch_size}</prop>
<prop key="hibernate.jdbc.batch_size">${hibernate.jdbc.batch_size}</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<context:annotation-config />
<jpa:repositories base-package="myproject.entity"
entity-manager-factory-ref="emf" transaction-manager-ref="transactionManager" />
</beans>
I guess that is due to the flush strategy. You should look into : https://vladmihalcea.com/how-does-the-auto-flush-work-in-jpa-and-hibernate/. to understand better.
Between this 2 version changes, it might happen that they have changed their flush strategy default. so first read the above document & configure your code as you want.