LuckyLuke
LuckyLuke

Reputation: 49077

How to obtain an entitymanager in Spring 3 like you do in Java EE 6

I have used Java EE 6 before and I have created a connection resource (pooled) on the server and then bound it to a JNDI name which I have referenced inside the jta-data-source element tag in the Persistence.xml file.

Now I use Spring 3 and I have a hard time understanding all the different beans that I need to set up and why need to do so. EJB 3 automatically wrap the methods in transactions. In Spring it seems like you need to configure a transaction manager, however I don't know. Need explanation.

<jee:jndi-lookup id="dataSource" jndi-name="jdbc/myapp" expected-type="javax.sql.DataSource"/>

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
    </property>
    <property name="packagesToScan" value="com.myapp.app"/>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">
                org.hibernate.dialect.PostgreSQL82Dialect
            </prop>
        </props>
    </property>
</bean>

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

<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />

I understand the jndi lookup for the data source, however I don't understand the rest thourougly. I am not able to insert/persist objects with this configuration.

I need an explanation on how Spring 3 differs from Java EE 6 in this area, and how to do it the same way.

Upvotes: 0

Views: 1067

Answers (1)

Mike
Mike

Reputation: 473

After thinking a little more on your original question, I'd like to add:

Spring doesn't automatically wrap every method with a Transaction. You have to tell Spring where you want your Transactional Boundaries to be. You do that with either XML config or by using the @Transactional annotation.

You should have a look at where you should declare transactions - here and here.

You should have a look at Spring's Transaction Management - here.

You should have a look at Transaction Config - here.

I am still of the opinion that your config is good and that the trouble you are having lies within the beans attempting to use your EntityManager. I renew my request for you to post that, as I'm certain I could get you going if I could see it.


I've looked at your configuration and I can't find anything wrong with it. As an example, I will provide (at the bottom) something I have in a working application right now.

I don't believe your problem is within your configuration, but in your usage of the configured beans. If you could provide code showing me (us) how you are interacting with your EntityManager, I could probably identify your issue(s).

I'll provide you with working examples of a Entity, Repository, and XML Config to try and help you locate your issue:

Spring Config (applicationContext.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                           http://www.springframework.org/schema/jee     http://www.springframework.org/schema/jee/spring-jee.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">
    <context:annotation-config />

    <context:component-scan base-package="com.company.app.dao" />
    <context:component-scan base-package="com.company.app.service" />

    <jee:jndi-lookup id="dataSource"
                     jndi-name="jdbc/Test" />

    <bean id="jpaDialect"
          class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />

    <bean id="jpaAdapter"
          class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />

    <bean id="entityManager" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource"       ref="dataSource" />
        <property name="jpaVendorAdapter" ref="jpaAdapter" />
        <property name="jpaDialect"       ref="jpaDialect" />
        <property name="packagesToScan" value="com.company.app.model" />
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager"
          class="org.springframework.orm.jpa.JpaTransactionManager"
          p:entityManagerFactory-ref="entityManager" />

    <tx:annotation-driven transaction-manager="transactionManager"
                          proxy-target-class="true" />
</beans>

Model

import org.hibernate.validator.constraints.Length;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

@Entity
public class User {
    @Id
    private String id = UUID.randomUUID().toString();

    @Column
    @Length(min = 3, max = 25)
    private String name;

    @OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER, orphanRemoval = true)
    @JoinColumn(name = "userId", nullable = false)
    private Set<Contact> contacts = new HashSet<Contact>();

    public UUID getId() {
        return UUID.fromString(id);
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<Contact> getContacts() {
        return contacts;
    }

    public void setContacts(Set<Contact> contacts) {
        this.contacts = contacts;
    }

    public void addContact(Contact contact) {
        this.contacts.add(contact);
    }

    public void removeContact(Contact contact) {
        this.contacts.remove(contact);
    }
}

Repository

import com.company.app.model.User;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaQuery;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

@Repository
public class UserDao {
    @PersistenceContext
    private EntityManager em;

    @Transactional(propagation = Propagation.REQUIRED)
    public void createUser(User user) {
        em.persist(user);
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
    public User readUserById(UUID id) {
        CriteriaQuery<User> query = em.getCriteriaBuilder().createQuery(User.class);
        query.where (em.getCriteriaBuilder().equal(query.from(User.class).get("id"),id.toString()));
        return em.createQuery(query).getSingleResult();
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
    public Set<User> readAll() {
        CriteriaQuery<User> query = em.getCriteriaBuilder().createQuery(User.class);
        query.from(User.class);
        return new HashSet<User>(em.createQuery(query).getResultList());
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public User update(User user) {
        return em.merge(user);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void delete(User user) {
        em.remove(user);
    }
}

Upvotes: 2

Related Questions