6

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>
Joe
  • 747
  • 3
  • 8
  • 21

1 Answers1

1

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.

Community
  • 1
  • 1
dube
  • 4,898
  • 2
  • 23
  • 41