JPA/EclipseLink 2 & Spring 3.0.2: The container policy [IndirectListContainerPolicy(class org.eclipse.persistence.indirection.IndirectList)] is not compatible with transparent indirection

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örgen Datenbanktabellen wurden automatisch erzeugt.

Leave a reply

Your email address will not be published.

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>