Kleines Beispiel zu JPA mit Spring 3.x, Hibernate 3.x, Lombok ,JUnit 4 und Maven 2.x

Thomas Darimont

Erfahrenes Mitglied
Hallo,

hier mal ein kleines Beispiel zur Verwendung von JPA mit Spring, Lombok ,JUnit und Maven 2.x realisiert mit der Spring Toolsuite (STS) IDE und mysql.

Unter: http://www.tutorials.de/java/385530...bernate-4-x-lombok-junit-4-und-maven-3-a.html
findet man ein neueres Beispiel mit Spring 3.1.x / Spring Data JPA, Hibernate 4, Junit 4 und Maven 3

Das Datenbank-Schema wird in diesem Beispiel automatisch generiert.

STS:
http://www.springsource.com/products/sts

Lombok:
http://projectlombok.org/

Spring:
http://www.springsource.org/

Maven:
http://maven.apache.org/

Junit:
http://junit.org/

Hibernate / EntityManager:
https://www.hibernate.org/397.html

Als Beispiel wähle ich hier eine kleine Benutzer/Rolenverwaltung. Die Beispiele sind mit
Absicht sehr knapp gehalten so dass man sie möglichst leicht für eigene Experimente
modifizieren kann.

Die Domain Klassen:

Unser User:
Java:
package de.tutorials.user;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;

import lombok.Data;

@Data
@Entity
public class User {
	@Id
	@GeneratedValue
	protected Long id;
	protected String name;

	@OneToMany
	protected Set<Role> roles = new HashSet<Role>();
}

Unsere Role:
Java:
package de.tutorials.user;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

import lombok.Data;

@Data
@Entity 
public class Role {
	@Id
	@GeneratedValue
	protected Long id;
	protected String name;
	
	public Role(String name) {
		this.name = name;
	}
}

Unser UserService Interface:
Java:
package de.tutorials.user.service;

import java.util.List;

import de.tutorials.user.User;

public interface IUserService {
	void register(User user);

	List<User> findAllUsers();
}

Unsere UserService Implementierung:
Java:
package de.tutorials.user.service.internal;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import de.tutorials.user.User;
import de.tutorials.user.persistence.IUserDao;
import de.tutorials.user.service.IUserService;

@Service
public class UserService implements IUserService{
	@Autowired
	IUserDao userDao;
	
	@Override
	public List<User> findAllUsers() {
		return userDao.findAllUsers();
	}

	@Override
	@Transactional
	public void register(User user) {
		userDao.save(user);
	}
}

Unser Dao-Interface:
Java:
package de.tutorials.user.persistence;

import java.util.List;

import de.tutorials.user.User;

public interface IUserDao {

	List<User> findAllUsers();

	void save(User user);
}

Unsere Dao-Implementierung:
Java:
package de.tutorials.user.persistence.internal;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.stereotype.Repository;

import de.tutorials.user.User;
import de.tutorials.user.persistence.IUserDao;

@Repository
public class UserDao implements IUserDao {

	@PersistenceContext
	private EntityManager em;

	@Override
	@SuppressWarnings("unchecked")
	public List<User> findAllUsers() {
		return em.createQuery("from User u").getResultList();
	}

	@Override
	public void save(User user) {
		em.persist(user);
	}

}

Unser Junit-TestCase:
Java:
package de.tutorials.user.service.internal;

import java.util.Iterator;
import java.util.List;

import javax.annotation.Resource;

import junit.framework.Assert;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;

import de.tutorials.user.Role;
import de.tutorials.user.User;
import de.tutorials.user.service.IUserService;


@TransactionConfiguration
@ContextConfiguration({"classpath:context.xml"})
@Transactional
@RunWith(SpringJUnit4ClassRunner.class)
public class UserServiceTest {
	
	@Resource
	IUserService userService;
	
	@Test public void registerUser(){
		User user = new User();
		user.setName("bubu");
		userService.register(user);
		
		List<User> allUsers = userService.findAllUsers();
		User savedUser = allUsers.get(0);
		Assert.assertEquals("There should be one saved user",1,allUsers.size());
		Assert.assertEquals("The user should be equal to saved user",user, savedUser);
	}
	
	@Test public void registerUserWithRoles(){
		User user = new User();
		
		Role roleAdmin = new Role("admin");
		Role roleUser = new Role("user");
		
		user.getRoles().add(roleAdmin);
		user.getRoles().add(roleUser);
		user.setName("bubu");
		
		userService.register(user);
		
		List<User> allUsers = userService.findAllUsers();
		User savedUser = allUsers.get(0);
		
		Assert.assertEquals("The user should be equal to saved user",user, savedUser);
		
		Assert.assertEquals("The user should have 2 roles",2, savedUser.getRoles().size());
		Assert.assertTrue("The user should have role user", savedUser.getRoles().contains(roleUser));
		Assert.assertTrue("The user should have role admin", savedUser.getRoles().contains(roleAdmin));
		
		Iterator<Role> iterRoles = savedUser.getRoles().iterator();
		
		Assert.assertEquals("First role should be admin",roleAdmin, iterRoles.next());
		Assert.assertEquals("Second role should be user",roleUser, iterRoles.next());
		
	}
}

Unsere Spring Konfigurations-Datei context.xml:
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:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jee="http://www.springframework.org/schema/jee" xmlns:lang="http://www.springframework.org/schema/lang"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
		http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"
	default-autowire="byName">

	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="username" value="root" />
		<property name="password" value="xxxxxxx" />
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost/training" />
	</bean>

	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="spring.training" />
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				<property name="showSql" value="true" />
				<property name="generateDdl" value="true" />
				<property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
			</bean>
		</property>
	</bean>


	<context:component-scan base-package="de.tutorials" />
	<context:annotation-config />

	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" />

	<tx:annotation-driven />

</beans>

Unsere "schlanke" persistence.xml im META-INF/: (die wahrscheinlich überflüssig ist, da die Persistence-Unit in der context.xml deklariert wird)
XML:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" 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_1_0.xsd">
	<persistence-unit name="spring.training">
	</persistence-unit>
</persistence>

Unser Maven pom.xml:
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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>de.tutorials</groupId>
  <artifactId>spring.training</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.11</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>3.4.0.GA</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>3.0.1.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
    	<groupId>org.slf4j</groupId>
    	<artifactId>slf4j-log4j12</artifactId>
    	<version>1.4.2</version>
    	<type>jar</type>
    	<scope>compile</scope>
    </dependency>
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-test</artifactId>
    	<version>3.0.1.RELEASE</version>
    	<type>jar</type>
    	<scope>compile</scope>
    </dependency>
    <dependency>
    	<groupId>junit</groupId>
    	<artifactId>junit</artifactId>
    	<version>4.7</version>
    	<type>jar</type>
    	<scope>compile</scope>
    </dependency>
    <dependency>
    	<groupId>cglib</groupId>
    	<artifactId>cglib</artifactId>
    	<version>2.2</version>
    	<type>jar</type>
    	<scope>compile</scope>
    </dependency>
  </dependencies>
</project>

Gruß Tom
 

Anhänge

Zuletzt bearbeitet von einem Moderator:
Hallo,

ich hab dazu mal ne Frage. Und zwar aktualisiere ich gerade eine Webanwendung von JSF1.2 auf JSF 2.0. In diesem Zusammenhang stelle ich auch auf Spring 3 um. Ich arbeite zur Zeit mit einer Hibernate SessionFactory, sehe aber immer mehr Beispiele mit JPA. Was ich noch nicht verstanden habe, wie das Mapping von Eigenschaften und von Objekten mit den Tabellen/Tabellenspalten bei JPA gemacht wird

Habe zur Zeit sowas in meiner applicationcontext.xml:

<!-- DB-Datasource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="driverClass" value="${jdbc.driverClassName}"/>
<property name="initialPoolSize" value="3"/>
<property name="acquireIncrement" value="1"/>
<property name="minPoolSize" value="5"/>
<property name="maxPoolSize" value="15"/>
<property name="maxIdleTime" value="3600"/>
<property name="numHelperThreads" value="6"/>
<property name="unreturnedConnectionTimeout" value="2000"/> <!-- Zerstört Connections nach x sekunden für Connection-leaks -->
</bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="annotatedClasses">
<list>
<value>de.navino.bo.User</value>
<value>de.navino.bo.UserLog</value>
</list>
</property>
<property name="annotatedPackages">
<list>
<value>de.navino.bo</value>
</list>
</property>

<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.use_outer_join">${hibernate.use_outer_join}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.generate_statistics">true</prop>
</props>
</property>
</bean>

Und das Objekt sieht so aus:
@Entity
@Table(name="T_USER")
public class User implements Serializable {
.......
}

Sind das 2 Wege zum gleichen Ziel, oder warum sollte ich auf JPA umsteigen?

In meinem DaoImpl leitet ich von HibernateDaoSupport ab, und darin verwende ich zum Beispiel:
User user = (User) getHibernateTemplate().get(User.class, objectId);

Gibt es sowas dann auch in JPA?


Gruß
navino
 
Zuletzt bearbeitet:
Dieses Tutorial war mir eine grosse Hilfe bei der Implementierung von Lombok unter Spring 3.0.2/Maven/Hibernate/JPA.
This tutorial was a great help for me to implement the lombok technology into an Spring 3.0.2/Maven/Hibernate/JPA project.

Vielen Dank,
thank you very much,

Thomas Eilermann
http://www.purebase.de
 
Hi Tom,

da hast du einen super Artikel verfasst. Ich habe es direkt mal getestet und bin begeistert.
Lombok ist echt mal chillig und die Integration von Maven, Spring, JPA in so komprimierter Form ist echt klasse - Dickes Lob^^

Das einzige Problem (zuanfangs) war, dass ich die Lombok Doku nicht ganz gelesen hab.:-(
Deshalb an alle anderen der Tipp:
Lombok muss in die IDE intergriert werden -> Doppelklick aufs Jar-Archive kann Wunder wirken^^

Aber eine Frage bezüglich des "registerUserWithRoles" Tests habe ich noch:
Die Beziehung User -> Role in User.java
Code:
    @OneToMany
    protected Set<Role> roles = new HashSet<Role>();
stellt doch eine mathematische Abstraktion einer Menge dar, wenn sie als HashSet realisiert ist. Meiner Meinung nach müsste das List Interface mit der LinkedList Implementierung gewäht werden, um eine sortierte Reihenfolge zu gewährleisten?

Thx,
Claus
 
Hallo,

danke :)

Meiner Meinung nach müsste das List Interface mit der LinkedList Implementierung gewäht werden, um eine sortierte Reihenfolge zu gewährleisten?
Lass User Comparable<User> Implementieren und verwende ein TreeSet anstatt eines HashSets wenn du eine geordnete Menge brauchst.

Ich habe ein Set anstatt einer List gewählt, da Hibernate je nachdem unterschiedliche Abfragen (bei Änderungen generiert). Bei einer Liste wird mehr abgefragt, da man noch die Position des Elements mitabruft.

Gruß Tom
 
also unter JBoss klappt das schon mal nicht, die persistence.xml ist ungültig.
Specification violation [EJB3 JPA 6.2.1.2] - You have not defined a jta-data-source
 
Hallo,

das Beispiel war eigentlich für den Standalone Einsatz gedacht...welche JBoss Version verwendest du?

Steht im Stacktrace zufällig noch eine Meldung bezüglich XML Parser? Wie hier:
http://serverfault.com/questions/43...pplication-to-jboss-due-to-classcastexception

-> Versions Konflikt!

Ansonsten könntest du mal folgendes ausprobieren:
1) http://forum.springsource.org/showthread.php?t=66923
2) Wenn die Datasource dort unbedingt angegeben werden muss, dann verwende einfach die <jta-data-source>java:/DefaultDS</jta-data-source> -> in der Spring Kontext haben wir aber eine andere Datasource konfiguriert!

Gruß Tom
 
Hallo Thomas,

ich muss dir erst einmal ein dickes Dankeschön aussprechen, sowas habe ich schon lange gesucht. Man findet so viele Spring-Beispiele im Netz, die am Ende teilweise noch Fehler enthalten... Spring wird ja immer als leichtgewichtig beschrieben, aber wenn man die ganzen "kleinen" Beispiele zu sieht, verliert man so schnell die Übersicht... Wieso dies alles so gemacht wird :(

Ich wollte dich daher fragen, ob du dein Beispiel erweitern könntest, um es auf einem Tomcat laufen zu lassen! Ich wäre dir sehr sehr dankbar! Mein Problem u.a. ist, dass es beim Umstieg mehrere META-INF Verzeichnisse gibt (wie ich es bisher gesehen habe), die teilweise das selbe enthalten und wieder rum nicht, ich blicke da halt gar nicht mehr durch. Ich würde mich richtig freuen, wenn du dir dafür die Zeit nehmen könntest!

Vielen Dank und viele Grüße
MK
 
Zurück