Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@PrePersist is being called and modifies the entity, but is not written to the database

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>
like image 803
Joe Avatar asked Sep 19 '13 00:09

Joe


1 Answers

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.

like image 87
dube Avatar answered Oct 06 '22 01:10

dube