2
votes

I'm trying to make a Spring Data JPA project work. My data is not persisted upon a employeeManager.addEmployee(employee) (see below).

The entity:

package com.howtodoinjava.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import org.springframework.data.neo4j.annotation.Indexed;

@Entity
@Table(name = "EMPLOYEE")
@SuppressWarnings("SerializableClass")
public class EmployeeEntity {

    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Column(name = "FIRSTNAME")
    private String firstname;

    @Column(name = "LASTNAME")
    private String lastname;

    @Indexed
    @Column(name = "EMAIL")
    private String email;

    @Column(name = "TELEPHONE")
    private String telephone;

    public String getEmail() {
        return email;
    }

    public String getTelephone() {
        return telephone;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void setTelephone(String telephone) {
        this.telephone = telephone;
    }

    public String getFirstname() {
        return firstname;
    }

    public String getLastname() {
        return lastname;
    }

    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }

    public void setLastname(String lastname) {
        this.lastname = lastname;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

The service:

package com.howtodoinjava.service;

import com.howtodoinjava.entity.EmployeeEntity;
import com.howtodoinjava.repository.jpa.EmployeeJpaRepository;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.neo4j.helpers.collection.IteratorUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class EmployeeManagerJpa implements EmployeeManager {

    @Autowired
    private EmployeeJpaRepository employeeRepository;

    @Transactional
    public void addEmployee(EmployeeEntity employee) {
        employeeRepository.save(employee);
    }

    @Transactional
    public List<EmployeeEntity> getAllEmployees() {
        return (List<EmployeeEntity>) makeCollection(employeeRepository.findAll());
    }

    @Transactional
    public void deleteEmployee(Long employeeId) {
        employeeRepository.delete(getEmployee(employeeId));
    }

    @Transactional
    public EmployeeEntity getEmployee(Long employeeId) {
        return employeeRepository.findOne(employeeId);
    }

    public static <E> Collection<E> makeCollection(Iterable<E> iter) {
        Collection<E> list = new ArrayList<E>();
        for (E item : iter) {
            list.add(item);
        }
        return list;
    }
}

The repository:

package com.howtodoinjava.repository.jpa;

import com.howtodoinjava.entity.EmployeeEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.CrudRepository;

public interface EmployeeJpaRepository extends JpaRepository<EmployeeEntity, Long> {}

The application context is split into two files (read in the context-param of the web.xml as employee-servlet-*.xml): The employee-servlet-app.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa" 
       xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
       xmlns:tx="http://www.springframework.org/schema/tx" 
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd            
            http://www.springframework.org/schema/data/jpa
            http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
            http://www.springframework.org/schema/data/neo4j
            http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <context:spring-configured/>    
    <context:component-scan base-package="com.howtodoinjava">
        <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>

    <tx:annotation-driven mode="proxy" transaction-manager="transactionManager" />

    <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/view/" />
        <property name="suffix" value=".jsp" />
    </bean> 
</beans>

The employee-servlet-jpa.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa" 
       xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
       xmlns:tx="http://www.springframework.org/schema/tx" 
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd            
            http://www.springframework.org/schema/data/jpa
            http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <jpa:repositories base-package="com.howtodoinjava.repository.jpa" />

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" > 
        <property name="packagesToScan" value="com.howtodoinjava.entity" />
        <property name="persistenceUnitName" value="SpringDataJPA" />
        <!--property name="dataSource" ref="dataSource" /-->
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider" />
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

</beans>

And finally, the persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

    <persistence-unit name="SpringDataJPA" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>      
        <properties>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/atom" />
            <property name="javax.persistence.jdbc.user" value="root" />
            <property name="javax.persistence.jdbc.password" value="123456" />

            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.hbm2ddl.auto" value="update" />

        </properties>
    </persistence-unit>
</persistence>

I'm getting this on the console:

Aug 27, 2014 3:06:48 AM org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [
    name: SpringDataJPA
    ...]
Aug 27, 2014 3:06:48 AM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.3.6.Final}
Aug 27, 2014 3:06:48 AM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Aug 27, 2014 3:06:48 AM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Aug 27, 2014 3:06:48 AM org.hibernate.annotations.common.reflection.java.JavaReflectionManager <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.5.Final}
Aug 27, 2014 3:06:48 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH000402: Using Hibernate built-in connection pool (not for production use!)
Aug 27, 2014 3:06:48 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/atom]
Aug 27, 2014 3:06:48 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH000046: Connection properties: {user=root, password=****}
Aug 27, 2014 3:06:48 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH000006: Autocommit mode: false
Aug 27, 2014 3:06:48 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
Aug 27, 2014 3:06:48 AM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
Aug 27, 2014 3:06:48 AM org.hibernate.engine.jdbc.internal.LobCreatorBuilder useContextualLobCreation
INFO: HHH000423: Disabling contextual LOB creation as JDBC driver reported JDBC version [3] less than 4
Aug 27, 2014 3:06:49 AM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
Aug 27, 2014 3:06:49 AM org.hibernate.tool.hbm2ddl.SchemaUpdate execute
INFO: HHH000228: Running hbm2ddl schema update
Aug 27, 2014 3:06:49 AM org.hibernate.tool.hbm2ddl.SchemaUpdate execute
INFO: HHH000102: Fetching database metadata
Aug 27, 2014 3:06:49 AM org.hibernate.tool.hbm2ddl.SchemaUpdate execute
INFO: HHH000396: Updating schema
Aug 27, 2014 3:06:49 AM org.hibernate.tool.hbm2ddl.TableMetadata <init>
INFO: HHH000261: Table found: atom.employee
Aug 27, 2014 3:06:49 AM org.hibernate.tool.hbm2ddl.TableMetadata <init>
INFO: HHH000037: Columns: [firstname, telephone, id, email, lastname]
Aug 27, 2014 3:06:49 AM org.hibernate.tool.hbm2ddl.TableMetadata <init>
INFO: HHH000108: Foreign keys: []
Aug 27, 2014 3:06:49 AM org.hibernate.tool.hbm2ddl.TableMetadata <init>
INFO: HHH000126: Indexes: [primary]
Aug 27, 2014 3:06:49 AM org.hibernate.tool.hbm2ddl.SchemaUpdate execute
INFO: HHH000232: Schema update complete
Hibernate: select employeeen0_.ID as ID1_0_, employeeen0_.EMAIL as EMAIL2_0_, employeeen0_.FIRSTNAME as FIRSTNAM3_0_, employeeen0_.LASTNAME as LASTNAME4_0_, employeeen0_.TELEPHONE as TELEPHON5_0_ from EMPLOYEE employeeen0_
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.519 sec

The table is created properly, it's just the save() method not persisting my data. I created a small JUnit test where I check after calling: employeeManager.addEmployee(employee); if the number of records has increased or not.

/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */

package com.howtodoinjava;

import com.howtodoinjava.entity.EmployeeEntity;
import com.howtodoinjava.service.EmployeeManager;
import junit.framework.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath*:employee-servlet*.xml")
@Transactional
public class EmployeeManagerTest {

    @Test
    public void emptyTest() {
    }

    @Autowired
    @Qualifier("employeeManagerJpa")
    EmployeeManager employeeManager;

    @Test
    public void test() {
        EmployeeEntity employee = new EmployeeEntity();
        employee.setFirstname("Tom");
        employee.setLastname("One");
        employee.setTelephone("12-154-789");
        employee.setEmail("[email protected]");

        EmployeeEntity employee1 = new EmployeeEntity();
        employee1.setFirstname("Kat");
        employee1.setLastname("Two");
        employee1.setTelephone("12-154-789");
        employee1.setEmail("[email protected]");

        employeeManager.addEmployee(employee);
        employeeManager.addEmployee(employee1);

        Assert.assertEquals("Two",employee1.getLastname()); // True

        Assert.assertEquals(0, employeeManager.getAllEmployees().size()); // Always true
    }
}

EDIT: If I force writing using saveAndFlush() I get the error of no transaction: org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress

I checked most fixes found on the web (including <x:annotation-driven /> parameters, @Transactional parameters, removing the @Service annotation and creating explicitly the bean in the employee-servlet-app.xml context file).

Any other suggestions are strongly appreciated. Feel free to ask for any other info that may help.

1
can you add your test case code? At a guess I'd say it's not establishing the transaction context correctly - stringy05
Thanks for the prompt reply. Tests added. - Infogeek
To your test, you need to add the TransactionalTestExecutionListener like in this gist. - Arturo Volpe
Same thing, the tests finished with no errors and 0 entities persisted. - Infogeek
attach your EmployeeJpaRepository.java, I guess the problem is over there! - Laurence Geng

1 Answers

0
votes

In your persistence.xml you have

<persistence-unit name="SpringDataJPA" transaction-type="RESOURCE_LOCAL">

Shouldn't the transaction type be JTA?

The default behavior, afaik, is that a transaction is always created automatically, no matter what, but it's read-only. So if there's something wrong with your config (or code) then nothing will be persisted.