I am developing an application in Spring MVC using Hibernate and MySQL and I have an issue. I am trying to populate my last modified field in my Java entity using the @PrePersist
annotation. I have debugged the code and the method is getting called and the value is getting set. However, the database is throwing a null violation, since it is not writing out the values the @PrePersist
method added. Does anyone know how to fix this t write out the data to the database?
FYI, besides changing dates, I want to use these JPA annotations to do certain business logic or use something similar to the annotations.
The code:
@Entity
@Table(name = "account")
public class Account {
@Column(name = "modified_on")
@Temporal(TemporalType.TIMESTAMP)
@DateTimeFormat(style = "MM")
@NotNull()
private Calendar modifiedOn;
... getters, setter and other stuff
@PrePersist
public void prePersist() {
Calendar now = Calendar.getInstance();
this.createdOn = now;
this.modifiedOn = now;
}
@PreUpdate
public void preUpdate() {
Calendar now = Calendar.getInstance();
this.modifiedOn = now;
}
applicationContext-Persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<repositories base-package="${repositoryPackageName}" />
<beans:bean id="hibernateJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
<beans:bean id="exceptionTranslator" class="org.springframework.orm.hibernate4.HibernateExceptionTranslator" />
<beans:bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<beans:property name="entityManagerFactory" ref="entityManagerFactory" />
</beans:bean>
<beans:bean id="persistenceExceptionTranslationPostProcessor" class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<beans:property name="driverClassName" value="${database.driverClassName}" />
<beans:property name="url" value="${database.url}" />
<beans:property name="username" value="${database.username}" />
<beans:property name="password" value="${database.password}" />
</beans:bean>
<beans:bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<beans:property name="dataSource" ref="dataSource" />
<beans:property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
<beans:property name="packagesToScan" value="${scanPackageName}" />
<beans:property name="jpaProperties">
<beans:props>
<beans:prop key="hbm2ddl.auto">validate</beans:prop>
<beans:prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</beans:prop>
<beans:prop key="hibernate.query.substitutions">true '1', false '0'</beans:prop>
<beans:prop key="hibernate.generate_statistics">true</beans:prop>
<beans:prop key="hibernate.show_sql">false</beans:prop>
<beans:prop key="hibernate.format_sql">true</beans:prop>
<beans:prop key="hibernate.hbm2ddl.auto">validate</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
</beans:beans>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation=
"http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
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/jee
http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.2.xsd"
>
<context:property-placeholder location="classpath:META-INF/spring/*.properties" />
<context:property-placeholder location="classpath:META-INF/properties/*.properties"/>
<context:component-scan base-package="${doaminPackageName}"/>
<context:component-scan base-package="${repositoryPackagename}"/>
<context:component-scan base-package="${repositoryPackageName}" >
<context:exclude-filter type="custom" expression="ourapp.util.TestClassFilter"/>
</context:component-scan>
<context:component-scan base-package="${utilBeanPackageName}"/>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="${mail.server.host}" />
<property name="port" value="${mail.server.port}" />
<property name="protocol" value="${mail.server.protocol}" />
<property name="username" value="${mail.server.username}" />
<property name="password" value="${mail.server.password}" />
<property name="javaMailProperties">
<util:properties location="classpath:META-INF/spring/javamail.properties" />
</property>
</bean>
</beans>
applicationContext-web.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation=
"http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
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/jee
http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.2.xsd"
>
<context:component-scan base-package="${controllerPackageName}"/>
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
<mvc:resources location="/, classpath:/META-INF/web-resources/" mapping="/resources/**"/>
<mvc:interceptors>
<bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" p:paramName="lang"/>
</mvc:interceptors>
<mvc:view-controller path="/uncaughtException"/>
<mvc:view-controller path="/resourceNotFound"/>
<mvc:view-controller path="/dataAccessFailure"/>
<bean class="org.springframework.context.support.ReloadableResourceBundleMessageSource" id="messageSource" p:fallbackToSystemLocale="false">
<property name="basenames">
<list>
<value>META-INF/web-resources/i18n/accountCreation/messages</value>
<value>META-INF/web-resources/i18n/accountCreation/application</value>
<value>META-INF/web-resources/i18n/accountCreation/errors</value>
<!-- Keep Global resource bundles at the bottom, they are checked last -->
<value>META-INF/web-resources/i18n/global_messages</value>
<value>META-INF/web-resources/i18n/global_application</value>
<value>META-INF/web-resources/i18n/global_errors</value>
</list>
</property>
</bean>
<bean class="org.springframework.web.servlet.i18n.CookieLocaleResolver" id="localeResolver" p:cookieName="locale">
<property name="defaultLocale" value="en"/>
</bean>
<bean class="org.springframework.ui.context.support.ResourceBundleThemeSource" id="themeSource"/>
<bean class="org.springframework.web.servlet.theme.CookieThemeResolver" id="themeResolver" p:cookieName="theme" p:defaultThemeName="standard"/>
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" p:defaultErrorView="exceptions/uncaughtException">
<property name="exceptionMappings">
<props>
<prop key=".DataAccessException">exceptions/dataAccessFailure</prop>
<prop key=".NoSuchRequestHandlingMethodException">exceptions/resourceNotFound</prop>
<prop key=".TypeMismatchException">exceptions/resourceNotFound</prop>
<prop key=".MissingServletRequestParameterException">exceptions/resourceNotFound</prop>
</props>
</property>
</bean>
<!-- Enable this for integration of file upload functionality -->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver"/>
<!-- TymeLeaf Settings -->
<!-- THYMELEAF: Template Resolver for webapp pages -->
<bean class="org.thymeleaf.templateresolver.ServletContextTemplateResolver" id="templateResolver">
<property name="prefix" value="/WEB-INF/templates/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
</bean>
<!-- THYMELEAF: Template Engine (Spring3-specific version) -->
<bean class="org.thymeleaf.spring3.SpringTemplateEngine" id="templateEngine">
<qualifier value="templateEngine"/>
<property name="templateResolver" ref="templateResolver"/>
</bean>
<!-- THYMELEAF: View Resolver - implementation of Spring's ViewResolver interface -->
<bean class="org.thymeleaf.spring3.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine"/>
</bean>
</beans>
If your application is configured to hook into the methods of your JPA entity, it might not track the changes if you set the fields directly. Try using the setter methods:
@PrePersist
public void prePersist() {
Calendar now = Calendar.getInstance();
this.setCreatedOn(now);
this.setModifiedOn(now);
}
@PreUpdate
public void preUpdate() {
Calendar now = Calendar.getInstance();
this.setModifiedOn(now);
}
Also, as stated in the answer of a simily question, those annotations behavior is implementation-dependent:
Note that it is implementation-dependent as to whether PreUpdate and PostUpdate call- backs occur when an entity is persisted and subsequently modified in a single transaction or when an entity is modified and subsequently removed within a single transaction. Portable applications should not rely on such behavior.
So it might depend on how you load and persist your entities.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With