Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity not persisting via Spring(CrudRepository) on TomEE

I have issues trying to persist an Entity to a PostgreSQL Database using Springs CrudRepository interface. I have had quite some issues setting this up with the correct values both on TomEE and adjusting the configuration of Spring itself. I tried briefly to use hibernate, but issues just got worse, so I switched back to OpenJpa that is bundled with TomEE.

I have a bit of experience with plain JavaEE and am creating this application to learn Spring and a friend of mine needs a Spring application to fool around with deployment on a TomEE based ApplicationServer, thus those two are requirements. I might go a bit overboard with the amount of code I attach, but I rather let you see what is there so you get a good overview.

Components I have developed and a brief description of what I'm doing, expecting and actually seeing. Components:

  • CoworkerController (RESTful Spring Controller)
  • CoworkerService (Spring Service to interact with Controller and Repository)
  • CoworkerRepository (extends Spring CrudRepository)
  • Coworker (Entity that is supposed to be persisted)

Flow of my actions:

  • Send a POST via curl with firstname lastname to the RESTful service
  • Getting a Status 200 with the ID of the persisted Entity
  • Checking PostgreSQL and RESTful service if I can query for my created Coworker

What I'm expecting:

  • Controller accepts POST and calls Service to persist the Coworker
  • Service tells Repository to save the Coworker
  • Repository persists Coworker Entity via Spring magic
  • Spring/OpenJPA magic that connects to DataSource, create ID and return Entity on success
  • See SQL queries during that happening
  • Repository receives persisted Entity and returns to Service
  • Service returns Entity to Controller
  • Controller returns Entity.id to the caller of the REST service

What I see and what I don't see but would expect to see:

  • Controller works as expected
  • Service logs correct user info
  • @PrePersist creates a Date Object (Logger)
  • SQL statement logs about openjpa sequence queries
  • MISSING: No SQL INSERT statement about persisting my Entity
  • Repository returns new Coworker to Service
  • Service logs the supposed to be persisted Entity with creationDate and id(e.g. 1)
  • Controller returns id to caller of REST (e.g. 1)
  • Neither REST nor SQL Queries show any Coworker in the Database :(

My configuration files have gotten quite a mess after trying all different kinds of troubleshooting found via google, SO and lots of tutorials. After spending now several days trying to figure out what the issue is, I come now here and hope you might be able to point me to my error.

Coworker.java

@Entity
public class Coworker implements Serializable {

    @Transient
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long coworkerId;
    private String firstName;
    private String lastName;
    @Temporal(TemporalType.DATE)
    private Date creationDate;
    @Temporal(TemporalType.DATE)
    private Date lastUpdatedDate;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "coworker", fetch = FetchType.EAGER)
    private Set<Timeframe> timeframes = new HashSet<Timeframe>();

    @PrePersist
    private void setDateBeforePersisting() {
        logger.info("Inside prepersisting a Coworker");
        creationDate = new Date();
        // Set the last updated Date to the creation date
        lastUpdatedDate = (Date) creationDate.clone();
    }
    // ...snip.. Getters, Setters, equals, hashcode created by IDEA here
}

CoworkerController.java

@Controller
@RequestMapping(value = "coworker")
public class CoworkerController {
    @Autowired
    CoworkerService service;
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @RequestMapping(method = RequestMethod.POST)
    @ResponseBody
    public Long create(@RequestBody Coworker coworker) {
        logger.info("Post to create Coworker.");
        if (coworker == null) {
            logger.info("Coworker == null");
        } else {
            logger.info("Coworker.firstName: " + coworker.getFirstName() + " Coworker.lastName: " + coworker.getLastName());
        }
        final Coworker coworker1 = service.addNewCoworker(coworker);
        logger.info("Persisted coworker, id: " + coworker1.getCoworkerId());
        return coworker1.getCoworkerId();
    }
}

CoworkerServiceImpl.java

@Service
@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public class CoworkerServiceImpl implements CoworkerService  {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public Coworker addNewCoworker(Coworker coworker) {
        logger.info("Service saving coworker: " + coworker.getFirstName() + " " + coworker.getLastName());
        final Coworker save = repo.save(coworker);
        logger.info("Service saved coworker, is it != null? " + (save != null));
        logger.info("First name: " + save.getFirstName() + " Last name: " + save.getLastName() + " Date created: " + save.getCreationDate() + " id: " + save.getCoworkerId());
        return save;
    }

}

CoworkerRepository.java

public interface CoworkerRepository extends CrudRepository<Coworker, Long> {
    // Several queries here, but nothing that should interfer with
    // CrudRepository.save(S)
}

persistence.xml

<?xml version="1.0" encoding="UTF-8" ?>
<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" xmlns="http://java.sun.com/xml/ns/persistence">
    <persistence-unit name="timeregistration" transaction-type="RESOURCE_LOCAL">
        <non-jta-data-source>java:/comp/env/jdbc/timeregistration</non-jta-data-source>
        <class>org.xezz.timeregistration.model.Coworker</class>
        <class>org.xezz.timeregistration.model.Customer</class>
        <class>org.xezz.timeregistration.model.Project</class>
        <class>org.xezz.timeregistration.model.Timeframe</class>
    </persistence-unit>
</persistence>

timeregistration-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/tx
        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
        http://www.springframework.org/schema/data/jpa
        http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">

    <mvc:resources mapping="/static/**" location="/static/"/>
    <mvc:default-servlet-handler/>
    <mvc:annotation-driven />
    <jpa:repositories base-package="org.xezz.timeregistration.repositories"/>
    <context:component-scan base-package="org.xezz.timeregistration" />
    <tx:annotation-driven transaction-manager="transactionManager"/>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="persistenceUnitName" value="timeregistration"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter">
                <property name="generateDdl" value="true"/>
                <property name="database" value="POSTGRESQL"/>
                <property name="showSql" value="true"/>
            </bean>
        </property>
    </bean>
    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="java:/comp/env/jdbc/timeregistration"/>
        <property name="resourceRef" value="true" />
    </bean>
    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="mediaTypes">
            <map>
                <entry key="html" value="text/html"/>
                <entry key="json" value="application/json"/>
            </map>
        </property>
        <property name="viewResolvers">
            <list>
                <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
                    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
                    <property name="prefix" value="/WEB-INF/jsp/"/>
                    <property name="suffix" value=".jsp"/>
                </bean>
            </list>
        </property>
        <property name="defaultViews">
            <list>
                <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
                    <property name="prefixJson" value="true"/>
                </bean>
            </list>
        </property>
    </bean>
</beans>

Consolelog

Mai 09, 2013 11:16:03 PM org.xezz.timeregistration.controller.CoworkerController create
Information: Post to create Coworker.
Mai 09, 2013 11:16:03 PM org.xezz.timeregistration.controller.CoworkerController create
Information: Coworker.firstName: Bastian Coworker.lastName: Koch
Mai 09, 2013 11:16:03 PM org.xezz.timeregistration.services.impl.CoworkerServiceImpl addNewCoworker
Information: Service saving coworker: Bastian Koch
Mai 09, 2013 11:16:03 PM org.xezz.timeregistration.model.Coworker setDateBeforePersisting
Information: Inside prepersisting a Coworker
Mai 09, 2013 11:16:03 PM org.xezz.timeregistration.services.impl.CoworkerServiceImpl addNewCoworker
Information: Service saved coworker, is it != null? true
59370  timeregistration  TRACE  [http-bio-8080-exec-1] openjpa.jdbc.SQL - <t 1732295982, conn 850080749> executing prepstmnt 553961214 SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? FOR UPDATE [params=?]
59421  timeregistration  TRACE  [http-bio-8080-exec-1] openjpa.jdbc.SQL - <t 1732295982, conn 850080749> [50 ms] spent
59423  timeregistration  TRACE  [http-bio-8080-exec-1] openjpa.jdbc.SQL - <t 1732295982, conn 683618612> executing prepstmnt 540500434 INSERT INTO OPENJPA_SEQUENCE_TABLE (ID, SEQUENCE_VALUE) VALUES (?, ?) [params=?, ?]
59474  timeregistration  TRACE  [http-bio-8080-exec-1] openjpa.jdbc.SQL - <t 1732295982, conn 683618612> [50 ms] spent
59476  timeregistration  TRACE  [http-bio-8080-exec-1] openjpa.jdbc.SQL - <t 1732295982, conn 334967428> executing prepstmnt 512431903 SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? FOR UPDATE [params=?]
59479  timeregistration  TRACE  [http-bio-8080-exec-1] openjpa.jdbc.SQL - <t 1732295982, conn 334967428> [3 ms] spent
59480  timeregistration  TRACE  [http-bio-8080-exec-1] openjpa.jdbc.SQL - <t 1732295982, conn 334967428> executing prepstmnt 1033569251 UPDATE OPENJPA_SEQUENCE_TABLE SET SEQUENCE_VALUE = ? WHERE ID = ? AND SEQUENCE_VALUE = ? [params=?, ?, ?]
59493  timeregistration  TRACE  [http-bio-8080-exec-1] openjpa.jdbc.SQL - <t 1732295982, conn 334967428> [12 ms] spent
Mai 09, 2013 11:16:04 PM org.xezz.timeregistration.services.impl.CoworkerServiceImpl addNewCoworker
Information: First name: Bastian Last name: Koch Date created: Thu May 09 23:16:03 CEST 2013 id: 1
Mai 09, 2013 11:16:04 PM org.xezz.timeregistration.controller.CoworkerController create
Information: Persisted coworker, id: 1

tomee.xml

<?xml version="1.0" encoding="UTF-8"?>
<tomee>
    <Resource id="jdbc/timeregistration" type="DataSource">
        JdbcDriver org.postgresql.Driver
        JdbcUrl jdbc:postgresql://localhost/timeregdb
        UserName timereg
        Password supersecretpassword
        jtaManaged false
        InitialSize 0
        MaxWait 50
        MaxActive 20
        MaxIdle 20
    </Resource>
</tomee>

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.xezz</groupId>
    <artifactId>timeregistration</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <junit.version>4.11</junit.version>
        <javax-persistence.version>2.0.0</javax-persistence.version>
        <javax-inject.version>1</javax-inject.version>
        <spring.version>3.2.2.RELEASE</spring.version>
        <spring-data.version>1.3.1.RELEASE</spring-data.version>
        <jackson.version>2.1.4</jackson.version>
        <jackson-mapper-asl.version>1.9.12</jackson-mapper-asl.version>
        <jaxb-api.version>2.2.7</jaxb-api.version>
        <slf4j-api.version>1.7.1</slf4j-api.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j-api.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>javax.persistence</artifactId>
            <version>${javax-persistence.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>${spring-data.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>${javax-inject.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>${jstl.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>${jackson-mapper-asl.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>${jaxb-api.version}</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Thanks for any help in advance, I have wasted so much time right now trying to track down different errors (especially following hibernate stuff, which was more time consuming than I dare to tell), so right now I just have to ask someone else to look over this and share wisdom with me and don't hesitate if you need more information (I can update github with the current codebase if needed).

like image 731
BKoch Avatar asked May 09 '13 22:05

BKoch


People also ask

What is crudrepository in spring?

CrudRepository is a Spring Data interface for generic CRUD operations on a repository of a specific type. It provides several methods out of the box for interacting with a database. In this tutorial, we'll explain how and when to use the CrudRepository save() method.

What is the use of save () method in crudrepository?

The save () method returns the saved entity, including the updated id field. 5. CrudRepository save () to Update an Instance We can use the same save () method to update an existing entry in our database. Suppose we saved a MerchandiseEntity instance with a specific title:

How do I update a crudrepository instance?

CrudRepository save () to Update an Instance We can use the same save () method to update an existing entry in our database. Suppose we saved a MerchandiseEntity instance with a specific title: Later, we found that we wanted to update the price of the item.

Why doesn't repository save have createdby and createddate in it?

Unfortunately, when I call repository.save () to update an existing entity the object returned does not have the createdBy and createdDate set. All the fields are set correctly in the database though This behavior is not caused by the auditing feature setting values or not. You are essentially resetting the fields to null.


1 Answers

I think you are using the wrong transactionManager in your spring configuration xml. Since you want to use spring with jpa, you should use the JpaTransactionManager.

For your example, I think your configuration should look as follows:

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
like image 71
Andreas Avatar answered Oct 15 '22 07:10

Andreas