Nachdem ich mir die letzten beiden Tage ein paar Sachen zu JPA und JTA durchgelesen hatte, setzte ich eine kleine Test-Anwendung auf, die mir mit Hilfe von Spring 3.0.2 und JPA/EclipseLink 2 Benutzer und deren Gruppen aus einer Datenbank laden sollte.
Das Datenbankschema dazu war einfach: Tabelle user, Tabelle group – zwischen beiden besteht eine n:m-Beziehung (Zwischentabelle user_in_group)
persistence.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="WebDefault" transaction-type="RESOURCE_LOCAL"> <class>de.ecw.project.sql.vo.UserVO</class> <class>de.ecw.project.sql.vo.GroupVO</class> <properties> <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/> <property name="eclipselink.ddl-generation.output-mode" value="both"/> </properties> </persistence-unit> </persistence>GroupVO.java
package de.ecw.project.sql.vo; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; import de.ecw.project.sql.Schema; @Entity @Table(name = Schema.GROUP_TABLE) public class GroupVO { @Id @GeneratedValue @Column(name = Schema.COLUMN_ID) private long id; @Column(name = Schema.COLUMN_NAME) private String name; public void setId(long id) { this.id = id; } public long getId() { return id; } public void setName(String name) { this.name = name; } public String getName() { return name; } }UserVO.java
package de.ecw.project.sql.vo; import java.util.Collection; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OrderBy; import javax.persistence.Table; import org.springmodules.validation.bean.conf.loader.annotation.handler.Length; import org.springmodules.validation.bean.conf.loader.annotation.handler.NotBlank; import com.thoughtworks.xstream.annotations.XStreamAlias; import de.ecw.project.sql.Schema; @XStreamAlias("user") @Entity @Table(name = Schema.USER_TABLE) public class UserVO { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = Schema.COLUMN_ID) private long id; @NotBlank @Length(max = 255, min = 10) @Column(name = Schema.USER_COLUMN_USERNAME) private String username; @NotBlank @Length(max = 255, min = 5) @Column(name = Schema.COLUMN_NAME) private String name; @Column(name = Schema.USER_COLUMN_PASSWORD) private String password; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = Schema.USER_IN_GROUP_TABLE, joinColumns = @JoinColumn(name = Schema.COLUMN_USER_ID), inverseJoinColumns = @JoinColumn(name = Schema.COLUMN_GROUP_ID)) @OrderBy(Schema.COLUMN_ID + " DESC") private Collection<GroupVO> groups; public void setName(String name) { this.name = name; } public String getName() { return name; } public void setUsername(String username) { this.username = username; } public String getUsername() { return username; } public void setId(long id) { this.id = id; } public long getId() { return id; } public String toString() { return "user"; } public void setPassword(String password) { this.password = password; } public String getPassword() { return password; } public void setGroups(Collection<GroupVO> groups) { this.groups = groups; } public Collection<GroupVO> getGroups() { return groups; } }dispatcher-servlet.xml
... <context:component-scan base-package="de.ecw" /> <bean id="dataSource" name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="org.gjt.mm.mysql.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="" /> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter"> <property name="showSql" value="true" /> <property name="generateDdl" value="true" /> <property name="databasePlatform" value="org.eclipse.persistence.platform.database.MySQLPlatform" /> </bean> </property> <property name="loadTimeWeaver"> <bean class="org.springframework.instrument.classloading.SimpleLoadTimeWeaver" /> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="userDAO" name="userDAO" class="de.ecw.project.sql.dao.impl.UserDAOImpl"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> ...Wenn ich die Anwendung im Tomcat startete, wurden keine Fehler ausgeworfen – sobald aber das erste Mal auf die DAO-Schicht bzw. VOs zugegriffen wurde, schmiss mir EclipseLink folgenden Fehler:
Local Exception Stack: Exception [EclipseLink-148] (Eclipse Persistence Services - 2.0.0.v20091127-r5931): org.eclipse.persistence.exceptions.DescriptorException Exception Description: The container policy [IndirectListContainerPolicy(class org.eclipse.persistence.indirection.IndirectList)] is not compatible with transparent indirection. Mapping: org.eclipse.persistence.mappings.ManyToManyMapping[groups] Descriptor: RelationalDescriptor(User --> [DatabaseTable(user)])Bei Google ließ sich bis auf zwei Einträge dazu nichts finden; die in den Threads beschriebenen Lösungsvorschläge brachten auch nichts.
Wenn ich mir die Dokumentation von Spring komplett durchgelesen hätte, wäre ich auch schneller auf die Lösung gekommen:
- In der dispatcher-servlet.xml muss SimpleLoadTimeWeaver durch InstrumentationLoadTimeWeaver ersetzt werden
- Die Eclipse-Anwendung muss mit den VM Argumenten -javaagent:
<Pfad>/WebContent/WEB-INF/lib/org.springframework.instrument-3.0.2.RELEASE.jar gestartet werden. - Zusätzlich habe ich noch in der server.xml im Kontext der Anwendung (<Context…) den TomcatInstrumentableClassLoader eingebunden (<<Loader loaderClass=”org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader” useSystemClassLoaderAsParent=”false”/>)
Nach einem Neustart des Tomcats lief dann die Anwendung auch und die zugehörigen Datenbanktabellen wurden automatisch erzeugt.
0 Comments