Using mappedBy & getting "not a valid inverse relation"

3 messages Options
Embed this post
Permalink
Matthew Adams

Using mappedBy & getting "not a valid inverse relation"

Reply Threaded More More options
Print post
Permalink
Hi all,

NB:  All source for this example is attached and is pasted at the end
of this email, in case the attachment doesn't survive the mailing
list.  I've also entered the following JIRA to track this issue:
https://issues.apache.org/jira/browse/OPENJPA-1361

Synopsis:  bidirectional one-to-many relationship where the one side
references the objects on the many side using a concrete class
reference and the many side references the object on the one side
using an interface reference.  When specifying the mappedBy property
of the @OneToMany annotation, OpenJPA throws the error:

Collection field "example.model.Concrete.pebbles" declares that it is
mapped by "example.model.Pebble.buildingMaterial", but this is not a
valid inverse relation.

When I remove the mappedBy property, everything works fine, except
that OpenJPA creates an extra join table (Concrete_Pebble) where it
shouldn't, IMHO.  I have two questions, an answer to either one of
which will suffice:

1. Why can't I use mappedBy in this scenario?
2. How do I annotate things with @JoinColumn or similar that will
allow me to not have a join table and only the reference from Pebble
back to its BuildingMaterial?

Thanks,
Matthew
====================
package example.model;

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

import javax.persistence.CascadeType;
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.OneToMany;
import javax.persistence.Version;

@Entity
public class Concrete implements BuildingMaterial {

        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name = "id")
        private Long assignedId;

        @Version
        @Column(name = "version")
        private long assignedVersion;

        @OneToMany(cascade = { CascadeType.MERGE, CascadeType.REFRESH,
                        CascadeType.PERSIST }, fetch = FetchType.LAZY)
        public Set<Pebble> pebbles = new HashSet<Pebble>();

        public Long getId() {
                return assignedId;
        }

        public long getVersion() {
                return assignedVersion;
        }

        public Discriminator getDiscriminator() {
                return Discriminator.CONCRETE;
        }

        public void add(Pebble pebble) {
                this.pebbles.add(pebble);
                pebble.buildingMaterial = this;
        }

        public Set<Pebble> getPebbles() {
                return new HashSet<Pebble>(pebbles);
        }
}
===========================
package example.model;

import javax.persistence.CascadeType;
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.ManyToOne;
import javax.persistence.Version;

import org.apache.openjpa.persistence.jdbc.Strategy;

@Entity
public class Pebble implements PersistentlyIdentifiable {

        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name = "id")
        private Long assignedId;

        @Version
        @Column(name = "version")
        private long assignedVersion;

        @ManyToOne(cascade = { CascadeType.MERGE, CascadeType.REFRESH,
                        CascadeType.PERSIST }, fetch = FetchType.LAZY)
        @Strategy("example.openjpa.PersistentInterfaceValueHandler")
        protected BuildingMaterial buildingMaterial;

        public Long getId() {
                return assignedId;
        }

        public long getVersion() {
                return assignedVersion;
        }

        public void setBuildingMaterial(BuildingMaterial buildingMaterial) {
                buildingMaterial.add(this);
        }

        public Discriminator getDiscriminator() {
                return Discriminator.PEBBLE;
        }

        public BuildingMaterial getBuildingMaterial() {
                return buildingMaterial;
        }
}
============================
package example.model;

public interface PersistentlyIdentifiable {
        Comparable<?> getId();

        long getVersion();

        Discriminator getDiscriminator();
}
============================
package example.model;

import java.util.Set;

public interface BuildingMaterial extends PersistentlyIdentifiable {

        void add(Pebble pebble);

        Set<Pebble> getPebbles();
}
============================
package example.model;

import java.util.HashMap;
import java.util.Map;

/**
 * Enum used to store discriminators in the database for polymorphic references.
 */
public enum Discriminator {

        CONCRETE(Concrete.class), PEBBLE(Pebble.class);

        private static class X {
                public static final Map<String, Discriminator>
discriminatorsByClassName = new HashMap<String, Discriminator>();

                public static void storeByClassName(Discriminator discriminator) {
                        synchronized (discriminatorsByClassName) {
                                discriminatorsByClassName.put(discriminator.getClassName(),
                                                discriminator);
                        }
                }
        }

        private Discriminator(Class<?> clazz) {
                this.className = clazz.getName();
                X.storeByClassName(this);
        }

        private String className;

        public String getClassName() {
                return className;
        }

        public static Discriminator getDiscriminatorEnum(String className) {
                return X.discriminatorsByClassName.get(className);
        }

        public static String getDiscriminatorName(Class<?> clazz) {
                Discriminator d = getDiscriminatorEnum(clazz.getName());
                if (d == null) {
                        throw new IllegalArgumentException("Class " + clazz.getName()
                                        + " does not have a corresponding Discriminator");
                }
                return d.name();
        }

        public static Class<?> getDiscriminatorClassFor(String name)
                        throws ClassNotFoundException {

                return getDiscriminatorClassFor(name, Discriminator.class
                                .getClassLoader());
        }

        public static Class<?> getDiscriminatorClassFor(String name,
                        ClassLoader loader) throws ClassNotFoundException {

                return Class.forName(Discriminator.valueOf(name).getClassName(), true,
                                loader);
        }
}
===========================
package example.openjpa;

import java.sql.SQLException;

import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
import org.apache.openjpa.jdbc.kernel.JDBCStore;
import org.apache.openjpa.jdbc.meta.ValueMapping;
import org.apache.openjpa.jdbc.meta.strats.AbstractValueHandler;
import org.apache.openjpa.jdbc.schema.Column;
import org.apache.openjpa.jdbc.schema.ColumnIO;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.kernel.StoreContext;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.util.StoreException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import example.model.Discriminator;
import example.model.PersistentlyIdentifiable;

@SuppressWarnings("serial")
public class PersistentInterfaceValueHandler extends AbstractValueHandler {

        Logger log = LoggerFactory.getLogger(PersistentInterfaceValueHandler.class);

        @Override
        public Column[] map(ValueMapping valueMapping, String fieldName,
                        ColumnIO columnIo, boolean adapt) {
                Column discriminatorColumn = new Column();
                discriminatorColumn.setName(fieldName.toUpperCase() + "_DISCRIMINATOR");
                discriminatorColumn.setJavaType(JavaTypes.STRING);

                Column idColumn = new Column();
                idColumn.setName(fieldName.toUpperCase() + "_ID");
                idColumn.setJavaType(JavaTypes.LONG);

                return new Column[] { discriminatorColumn, idColumn };
        }

        public boolean isVersionable(ValueMapping vm) {
                return true;
        }

        public boolean objectValueRequiresLoad(ValueMapping vm) {
                return true;
        }

        public Object toDataStoreValue(ValueMapping vm, Object val, JDBCStore store) {
                if (val == null) {
                        return new Object[] { null, null };
                }
                return new Object[] { deriveDiscriminator(val.getClass()),
                                ((PersistentlyIdentifiable) val).getId() };
        }

        public String deriveDiscriminator(Class<?> clazz) {
                if (log.isDebugEnabled()) {
                        log.debug("Getting discriminator for class: [" + clazz.getName()
                                        + "]");
                }
                String discriminator = Discriminator.getDiscriminatorName(clazz);
                if (log.isDebugEnabled()) {
                        log.debug("Discriminator for class: [" + clazz.getName() + "]:  ["
                                        + discriminator + "]");
                }
                return discriminator;
        }

        public Class<?> deriveClass(String name, ClassLoader loader)
                        throws ClassNotFoundException {
                if (log.isDebugEnabled()) {
                        log.debug("Getting class for discriminator: [" + name + "]");
                }
                Class<?> clazz = Discriminator.getDiscriminatorClassFor(name, loader);
                if (log.isDebugEnabled()) {
                        log.debug("Class for discriminator: [" + name + "]:  ["
                                        + clazz.getName() + "]");
                }
                return clazz;
        }

        public Object toObjectValue(ValueMapping vm, Object val,
                        OpenJPAStateManager sm, JDBCStore store,
                        JDBCFetchConfiguration fetch) throws SQLException {

                if (val == null) {
                        return null;
                }
                Object[] values = (Object[]) val;
                StoreContext ctx = store.getContext();
                ClassLoader loader = store.getConfiguration()
                                .getClassResolverInstance().getClassLoader(vm.getType(),
                                                ctx.getClassLoader());
                Class<?> cls = null;
                String clsName = (String) values[0];
                Long oidStr = (Long) values[1];
                if (clsName == null) {
                        return null;
                }
                try {
                        cls = deriveClass(clsName, loader);
                } catch (ClassNotFoundException cnfe) {
                        throw new StoreException(cnfe);
                }
                Object oid = ctx.newObjectId(cls, oidStr);
                return store.find(oid, vm, fetch);
        }
}
========================
package example.test.integration;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import java.util.Set;

import org.junit.Test;
import org.springframework.transaction.annotation.Transactional;

import example.model.BuildingMaterial;
import example.model.Concrete;
import example.model.Pebble;

public class ConcretePebbleTest extends AbstractJpaTest {

        @Test
        @Transactional
        public void testConcrete() {

                BuildingMaterial bm = new Concrete();
                em.persist(bm);

                Pebble p = new Pebble();
                bm.add(p);

                em.flush();

                Object bmid = bm.getId();
                Object pid = p.getId();

                em.clear();

                // navigate from BuildingMaterials side
                bm = em.find(bm.getClass(), bmid);
                Set<Pebble> pebbles = null;
                assertNotNull(pebbles = bm.getPebbles());
                assertEquals(1, pebbles.size());

                em.clear();

                // navigate from Pebble side
                p = em.find(Pebble.class, pid);
                assertNotNull(bm = p.getBuildingMaterial());
        }
}
=======================
package example.test.integration;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@ContextConfiguration(locations={"classpath:applicationContext.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
abstract public class AbstractJpaTest {

        @Autowired
        protected EntityManagerFactory emf;
       
        @PersistenceContext
        protected EntityManager em;
       
        @PostConstruct
        public void afterPropertiesSet() throws Exception {
                emf.createEntityManager();
        }
}
========================
package example.test.unit;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

import example.model.Concrete;
import example.model.Discriminator;

public class DiscriminatorTest {

        @Test
        public void testDiscriminator() throws ClassNotFoundException {
                Discriminator d = Discriminator.CONCRETE;
                assertEquals(Discriminator.getDiscriminatorName(Concrete.class), d
                                .name());

                assertEquals(Discriminator.getDiscriminatorClassFor(d.name()),
                                Concrete.class);
        }
}
===========================
# log4j.properties
log4j.rootLogger=ERROR, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss.SSS} [%t] %-5p
%c %x - %m%n

log4j.logger.example=debug
===========================
# test.properties
test.database=my-integration-test
===========================
<!-- 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:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                           http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
                           http://www.springframework.org/schema/tx
  http://www.springframework.org/schema/tx/spring-tx-2.5.xsd

http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">

        <bean
                class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
                <property name="location" value="classpath:test.properties" />
        </bean>

        <bean id="HsqldbDataSource" class="org.hsqldb.jdbc.jdbcDataSource">
                <property name="user" value="sa" />
                <property name="database" value="jdbc:hsqldb:mem:${test.database}" />
        </bean>

        <bean id="entityManagerFactory"
                class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
                <property name="persistenceUnitName" value="case-sandbox" />
                <property name="jpaProperties">
                        <value>
                                openjpa.AutoDetach=commit
                                openjpa.DetachState=fgs
                                openjpa.QueryCache=false
                                openjpa.jdbc.SubclassFetchMode=none
                                openjpa.jdbc.EagerFetchMode=none
                                openjpa.jdbc.SynchronizeMappings=buildSchema(ForeignKeys=true)
      </value>
                </property>
                <property name="jpaVendorAdapter">
                        <bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter">
                                <property name="showSql" value="${test.showSql}" />
                                <property name="generateDdl" value="false" />
                        </bean>
                </property>
                <property name="dataSource" ref="HsqldbDataSource" />
        </bean>
        <bean
                class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"
/>

        <bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
                <property name="entityManagerFactory" ref="entityManagerFactory" />
                <property name="jpaDialect">
                        <bean class="org.springframework.orm.jpa.vendor.OpenJpaDialect" />
                </property>
                <property name="dataSource" ref="HsqldbDataSource" />
        </bean>
        <tx:annotation-driven transaction-manager="transactionManager"
                proxy-target-class="true" />

</beans>
=============================
<!-- orm.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
        http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
        version="1.0">

        <persistence-unit-metadata>
                <persistence-unit-defaults>
                        <access>FIELD</access>
                        <cascade-persist/>
                </persistence-unit-defaults>
        </persistence-unit-metadata>

</entity-mappings>
================================
<!-- persistence.xml -->
<?xml version="1.0"
      encoding="UTF-8"?>

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
        version="1.0">
        <persistence-unit name="case-sandbox">
                <provider>
            org.apache.openjpa.persistence.PersistenceProviderImpl
                </provider>
        </persistence-unit>
</persistence>
===============================
<!-- pom.xml -->
<?xml version="1.0"
      encoding="UTF-8"?>

<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>org.mytest</groupId>
        <artifactId>case-sandbox</artifactId>
        <packaging>jar</packaging>
        <version>1.0.0-SNAPSHOT</version>
        <name>Sandbox</name>
        <properties>
                <surefire.debug.suspend>n</surefire.debug.suspend>
                <test.showSql>true</test.showSql>
                <spring.version>2.5.6</spring.version>
                <slf4j.version>1.5.6</slf4j.version>
                <jpa.spec.version>1.0</jpa.spec.version>
                <openjpa.version>1.2.1</openjpa.version>
                <hsql.version>1.8.0.1</hsql.version>
        </properties>
        <dependencies>
                <dependency>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-log4j12</artifactId>
                        <version>${slf4j.version}</version>
                </dependency>
                <dependency>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-api</artifactId>
                        <version>${slf4j.version}</version>
                </dependency>
                <dependency>
                        <groupId>javax.persistence</groupId>
                        <artifactId>persistence-api</artifactId>
                        <version>${jpa.spec.version}</version>
                </dependency>
                <dependency>
                        <groupId>org.apache.openjpa</groupId>
                        <artifactId>openjpa</artifactId>
                        <version>${openjpa.version}</version>
                </dependency>
                <dependency>
                        <groupId>hsqldb</groupId>
                        <artifactId>hsqldb</artifactId>
                        <version>${hsql.version}</version>
                </dependency>
                <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring</artifactId>
                        <version>${spring.version}</version>
                </dependency>
                <dependency>
                        <groupId>junit</groupId>
                        <artifactId>junit</artifactId>
                        <version>4.6</version>
                </dependency>
                <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-test</artifactId>
                        <version>${spring.version}</version>
                        <exclusions>
                                <exclusion>
                                        <groupId>junit</groupId>
                                        <artifactId>junit</artifactId>
                                </exclusion>
                        </exclusions>
                </dependency>
                <dependency>
                        <groupId>cglib</groupId>
                        <artifactId>cglib-nodep</artifactId>
                        <version>2.1_3</version>
                </dependency>
                <!--
                <dependency>
                        <groupId>org.jmock</groupId>
                        <artifactId>jmock-junit4</artifactId>
                        <scope>test</scope>
                </dependency>
                -->
        </dependencies>
        <build>
                <testResources>
                        <testResource>
                                <filtering>true</filtering>
                                <directory>src/test/resources</directory>
                                <includes>
                                        <include>**/*</include>
                                </includes>
                        </testResource>
                </testResources>
        <plugins>
                <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-jar-plugin</artifactId>
                        <version>2.2</version>
                        <executions>
                                <execution>
                                        <goals>
                                                <goal>test-jar</goal>
                                        </goals>
                                </execution>
                        </executions>
                </plugin>
                        <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-compiler-plugin</artifactId>
                                <configuration>
                                        <source>1.6</source>
                                        <target>1.6</target>
                                </configuration>
                        </plugin>
                        <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-antrun-plugin</artifactId>
                                <version>1.1</version>
                                <executions>
                                        <execution>
                                                <id>1</id>
                                                <phase>process-classes</phase>
                                                <configuration>
                                                        <tasks>
                                                                <path id="cp">
                                                                        <path refid="maven.compile.classpath" />
                                                                </path>

                                                                <pathconvert pathsep=" " property="arguments"
                                                                        dirsep=".">
                                                                        <map from="${basedir}/target/classes/" to="" />
                                                                        <map from="${basedir}\target\classes\" to="" />
                                                                        <mapper>
                                                                                <chainedmapper>
                                                                                        <globmapper from="*.class" to="*" />
                                                                                </chainedmapper>
                                                                        </mapper>
                                                                        <path id="model.path">
                                                                                <fileset dir="${basedir}/target/classes/">
                                                                                        <include name="**/*.class" />
                                                                                        <exclude name="**/annotations/**/*.class" />
                                                                                </fileset>
                                                                        </path>
                                                                </pathconvert>
                                                                <java classname="org.apache.openjpa.enhance.PCEnhancer"
                                                                        classpathref="cp" dir="target/classes" fork="true">
                                                                        <arg line="${arguments} -p persistence.xml#case-sandbox" />
                                                                </java>
                                                        </tasks>
                                                </configuration>
                                                <goals>
                                                        <goal>run</goal>
                                                </goals>
                                        </execution>
                                        <execution>
                                                <id>2</id>
                                                <configuration>
                                                        <tasks>
                                                                <path id="cp">
                                                                        <path refid="maven.compile.classpath" />
                                                                </path>
                                                                        <java classname="org.apache.openjpa.jdbc.meta.MappingTool"
                                                                                classpathref="cp" dir="target/classes" fork="true">
                                                                       
                                                                                <arg line="-p persistence.xml#case-sandbox" />
                                                                                <arg line="-connectionDriverName org.hsqldb.jdbc.jdbcDataSource" />
                                                                                <arg line="-connectionURL jdbc:hsqldb:mem:dummy" />
                                                                                <arg line="-schemaAction build" />
                                                                                <arg line="-sql ../test.sql" />
                                                                        </java>
                                                                       
                                                        </tasks>
                                                </configuration>
                                                <phase>compile</phase>
                                                <goals>
                                                        <goal>run</goal>
                                                </goals>
                                        </execution>
                                       
                                </executions>
                        </plugin>
                        <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-surefire-plugin</artifactId>
                                <version>2.3</version>
                                <configuration>
                                        <argLine>-Xrunjdwp:transport=dt_socket,address=9998,server=y,suspend=${surefire.debug.suspend}
-Xmx512m</argLine>
                                        <includes>
                                                <include>**/*Test.java</include>
                                        </includes>
                                        <excludes>
                                                <exclude>**/integration/**</exclude>
                                        </excludes>
                                </configuration>
                                <executions>
                                        <execution>
                                                <id>integration-tests</id>
                                                <phase>integration-test</phase>
                                                <goals>
                                                        <goal>test</goal>
                                                </goals>
                                                <configuration>
                                                        <skip>false</skip>
                                                        <excludes>
                                                                <exclude>**/unit/**</exclude>
                                                        </excludes>
                                                        <includes>
                                                                <include>**/integration/**/*Test.java</include>
                                                        </includes>
                                                </configuration>
                                        </execution>
                                </executions>
                        </plugin>
                </plugins>
        </build>
</project>
================================


case-sandbox.zip (42K) Download Attachment
Michael Dick

Re: Using mappedBy & getting "not a valid inverse relation"

Reply Threaded More More options
Print post
Permalink
Hi Matthew,

<disclosure>I haven't tried the example you submitted. </disclosure>

1. If I had to guess I'd say that it's because the interface
(BuildingMaterial) doesn't have the @OneToMany annotation, and as a result
it's not a valid target for the relationship. Adding the annotatation (and
switching to property access instead of field access) might help here.

If you can't move to property access and add the annotation then I'm not
sure how you could use an interface.. Using a MappedSuperclass for
BuildingMaterial instead of an interface (could be an abstract
MappedSuperclass though).

2. I'd have to look into @JoinColumn, I suspect it would result in similar
behavior though (I'm prepared to be wrong here - haven't used JoinColumn
much).

Hope this helps
-mike

On Fri, Oct 23, 2009 at 12:54 PM, Matthew Adams <[hidden email]>wrote:

> Hi all,
>
> NB:  All source for this example is attached and is pasted at the end
> of this email, in case the attachment doesn't survive the mailing
> list.  I've also entered the following JIRA to track this issue:
> https://issues.apache.org/jira/browse/OPENJPA-1361
>
> Synopsis:  bidirectional one-to-many relationship where the one side
> references the objects on the many side using a concrete class
> reference and the many side references the object on the one side
> using an interface reference.  When specifying the mappedBy property
> of the @OneToMany annotation, OpenJPA throws the error:
>
> Collection field "example.model.Concrete.pebbles" declares that it is
> mapped by "example.model.Pebble.buildingMaterial", but this is not a
> valid inverse relation.
>
> When I remove the mappedBy property, everything works fine, except
> that OpenJPA creates an extra join table (Concrete_Pebble) where it
> shouldn't, IMHO.  I have two questions, an answer to either one of
> which will suffice:
>
> 1. Why can't I use mappedBy in this scenario?
> 2. How do I annotate things with @JoinColumn or similar that will
> allow me to not have a join table and only the reference from Pebble
> back to its BuildingMaterial?
>
> Thanks,
> Matthew
> ====================
> package example.model;
>
> import java.util.HashSet;
> import java.util.Set;
>
> import javax.persistence.CascadeType;
> 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.OneToMany;
> import javax.persistence.Version;
>
> @Entity
> public class Concrete implements BuildingMaterial {
>
>        @Id
>        @GeneratedValue(strategy = GenerationType.AUTO)
>        @Column(name = "id")
>        private Long assignedId;
>
>        @Version
>        @Column(name = "version")
>        private long assignedVersion;
>
>        @OneToMany(cascade = { CascadeType.MERGE, CascadeType.REFRESH,
>                        CascadeType.PERSIST }, fetch = FetchType.LAZY)
>        public Set<Pebble> pebbles = new HashSet<Pebble>();
>
>        public Long getId() {
>                return assignedId;
>        }
>
>        public long getVersion() {
>                return assignedVersion;
>        }
>
>        public Discriminator getDiscriminator() {
>                return Discriminator.CONCRETE;
>        }
>
>        public void add(Pebble pebble) {
>                this.pebbles.add(pebble);
>                pebble.buildingMaterial = this;
>        }
>
>        public Set<Pebble> getPebbles() {
>                return new HashSet<Pebble>(pebbles);
>        }
> }
> ===========================
> package example.model;
>
> import javax.persistence.CascadeType;
> 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.ManyToOne;
> import javax.persistence.Version;
>
> import org.apache.openjpa.persistence.jdbc.Strategy;
>
> @Entity
> public class Pebble implements PersistentlyIdentifiable {
>
>        @Id
>        @GeneratedValue(strategy = GenerationType.AUTO)
>        @Column(name = "id")
>        private Long assignedId;
>
>        @Version
>        @Column(name = "version")
>        private long assignedVersion;
>
>        @ManyToOne(cascade = { CascadeType.MERGE, CascadeType.REFRESH,
>                        CascadeType.PERSIST }, fetch = FetchType.LAZY)
>        @Strategy("example.openjpa.PersistentInterfaceValueHandler")
>        protected BuildingMaterial buildingMaterial;
>
>        public Long getId() {
>                return assignedId;
>        }
>
>        public long getVersion() {
>                return assignedVersion;
>        }
>
>        public void setBuildingMaterial(BuildingMaterial buildingMaterial) {
>                buildingMaterial.add(this);
>        }
>
>        public Discriminator getDiscriminator() {
>                return Discriminator.PEBBLE;
>        }
>
>        public BuildingMaterial getBuildingMaterial() {
>                return buildingMaterial;
>        }
> }
> ============================
> package example.model;
>
> public interface PersistentlyIdentifiable {
>        Comparable<?> getId();
>
>        long getVersion();
>
>        Discriminator getDiscriminator();
> }
> ============================
> package example.model;
>
> import java.util.Set;
>
> public interface BuildingMaterial extends PersistentlyIdentifiable {
>
>        void add(Pebble pebble);
>
>        Set<Pebble> getPebbles();
> }
> ============================
> package example.model;
>
> import java.util.HashMap;
> import java.util.Map;
>
> /**
>  * Enum used to store discriminators in the database for polymorphic
> references.
>  */
> public enum Discriminator {
>
>        CONCRETE(Concrete.class), PEBBLE(Pebble.class);
>
>        private static class X {
>                public static final Map<String, Discriminator>
> discriminatorsByClassName = new HashMap<String, Discriminator>();
>
>                public static void storeByClassName(Discriminator
> discriminator) {
>                        synchronized (discriminatorsByClassName) {
>
>  discriminatorsByClassName.put(discriminator.getClassName(),
>                                                discriminator);
>                        }
>                }
>        }
>
>        private Discriminator(Class<?> clazz) {
>                this.className = clazz.getName();
>                X.storeByClassName(this);
>        }
>
>        private String className;
>
>        public String getClassName() {
>                return className;
>        }
>
>        public static Discriminator getDiscriminatorEnum(String className) {
>                return X.discriminatorsByClassName.get(className);
>        }
>
>        public static String getDiscriminatorName(Class<?> clazz) {
>                Discriminator d = getDiscriminatorEnum(clazz.getName());
>                if (d == null) {
>                        throw new IllegalArgumentException("Class " +
> clazz.getName()
>                                        + " does not have a corresponding
> Discriminator");
>                }
>                return d.name();
>        }
>
>        public static Class<?> getDiscriminatorClassFor(String name)
>                        throws ClassNotFoundException {
>
>                return getDiscriminatorClassFor(name, Discriminator.class
>                                .getClassLoader());
>        }
>
>        public static Class<?> getDiscriminatorClassFor(String name,
>                        ClassLoader loader) throws ClassNotFoundException {
>
>                return
> Class.forName(Discriminator.valueOf(name).getClassName(), true,
>                                loader);
>        }
> }
> ===========================
> package example.openjpa;
>
> import java.sql.SQLException;
>
> import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
> import org.apache.openjpa.jdbc.kernel.JDBCStore;
> import org.apache.openjpa.jdbc.meta.ValueMapping;
> import org.apache.openjpa.jdbc.meta.strats.AbstractValueHandler;
> import org.apache.openjpa.jdbc.schema.Column;
> import org.apache.openjpa.jdbc.schema.ColumnIO;
> import org.apache.openjpa.kernel.OpenJPAStateManager;
> import org.apache.openjpa.kernel.StoreContext;
> import org.apache.openjpa.meta.JavaTypes;
> import org.apache.openjpa.util.StoreException;
> import org.slf4j.Logger;
> import org.slf4j.LoggerFactory;
>
> import example.model.Discriminator;
> import example.model.PersistentlyIdentifiable;
>
> @SuppressWarnings("serial")
> public class PersistentInterfaceValueHandler extends AbstractValueHandler {
>
>        Logger log =
> LoggerFactory.getLogger(PersistentInterfaceValueHandler.class);
>
>        @Override
>        public Column[] map(ValueMapping valueMapping, String fieldName,
>                        ColumnIO columnIo, boolean adapt) {
>                Column discriminatorColumn = new Column();
>                discriminatorColumn.setName(fieldName.toUpperCase() +
> "_DISCRIMINATOR");
>                discriminatorColumn.setJavaType(JavaTypes.STRING);
>
>                Column idColumn = new Column();
>                idColumn.setName(fieldName.toUpperCase() + "_ID");
>                idColumn.setJavaType(JavaTypes.LONG);
>
>                return new Column[] { discriminatorColumn, idColumn };
>        }
>
>        public boolean isVersionable(ValueMapping vm) {
>                return true;
>        }
>
>        public boolean objectValueRequiresLoad(ValueMapping vm) {
>                return true;
>        }
>
>        public Object toDataStoreValue(ValueMapping vm, Object val,
> JDBCStore store) {
>                if (val == null) {
>                        return new Object[] { null, null };
>                }
>                return new Object[] { deriveDiscriminator(val.getClass()),
>                                ((PersistentlyIdentifiable) val).getId() };
>        }
>
>        public String deriveDiscriminator(Class<?> clazz) {
>                if (log.isDebugEnabled()) {
>                        log.debug("Getting discriminator for class: [" +
> clazz.getName()
>                                        + "]");
>                }
>                String discriminator =
> Discriminator.getDiscriminatorName(clazz);
>                if (log.isDebugEnabled()) {
>                        log.debug("Discriminator for class: [" +
> clazz.getName() + "]:  ["
>                                        + discriminator + "]");
>                }
>                return discriminator;
>        }
>
>        public Class<?> deriveClass(String name, ClassLoader loader)
>                        throws ClassNotFoundException {
>                if (log.isDebugEnabled()) {
>                        log.debug("Getting class for discriminator: [" +
> name + "]");
>                }
>                Class<?> clazz =
> Discriminator.getDiscriminatorClassFor(name, loader);
>                if (log.isDebugEnabled()) {
>                        log.debug("Class for discriminator: [" + name + "]:
>  ["
>                                        + clazz.getName() + "]");
>                }
>                return clazz;
>        }
>
>        public Object toObjectValue(ValueMapping vm, Object val,
>                        OpenJPAStateManager sm, JDBCStore store,
>                        JDBCFetchConfiguration fetch) throws SQLException {
>
>                if (val == null) {
>                        return null;
>                }
>                Object[] values = (Object[]) val;
>                StoreContext ctx = store.getContext();
>                ClassLoader loader = store.getConfiguration()
>
>  .getClassResolverInstance().getClassLoader(vm.getType(),
>                                                ctx.getClassLoader());
>                Class<?> cls = null;
>                String clsName = (String) values[0];
>                Long oidStr = (Long) values[1];
>                if (clsName == null) {
>                        return null;
>                }
>                try {
>                        cls = deriveClass(clsName, loader);
>                } catch (ClassNotFoundException cnfe) {
>                        throw new StoreException(cnfe);
>                }
>                Object oid = ctx.newObjectId(cls, oidStr);
>                return store.find(oid, vm, fetch);
>        }
> }
> ========================
> package example.test.integration;
>
> import static org.junit.Assert.assertEquals;
> import static org.junit.Assert.assertNotNull;
>
> import java.util.Set;
>
> import org.junit.Test;
> import org.springframework.transaction.annotation.Transactional;
>
> import example.model.BuildingMaterial;
> import example.model.Concrete;
> import example.model.Pebble;
>
> public class ConcretePebbleTest extends AbstractJpaTest {
>
>        @Test
>        @Transactional
>        public void testConcrete() {
>
>                BuildingMaterial bm = new Concrete();
>                em.persist(bm);
>
>                Pebble p = new Pebble();
>                bm.add(p);
>
>                em.flush();
>
>                Object bmid = bm.getId();
>                Object pid = p.getId();
>
>                em.clear();
>
>                // navigate from BuildingMaterials side
>                bm = em.find(bm.getClass(), bmid);
>                Set<Pebble> pebbles = null;
>                assertNotNull(pebbles = bm.getPebbles());
>                assertEquals(1, pebbles.size());
>
>                em.clear();
>
>                // navigate from Pebble side
>                p = em.find(Pebble.class, pid);
>                assertNotNull(bm = p.getBuildingMaterial());
>        }
> }
> =======================
> package example.test.integration;
>
> import javax.annotation.PostConstruct;
> import javax.persistence.EntityManager;
> import javax.persistence.EntityManagerFactory;
> import javax.persistence.PersistenceContext;
>
> import org.junit.runner.RunWith;
> import org.springframework.beans.factory.annotation.Autowired;
> import org.springframework.test.context.ContextConfiguration;
> import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
>
> @ContextConfiguration(locations={"classpath:applicationContext.xml"})
> @RunWith(SpringJUnit4ClassRunner.class)
> abstract public class AbstractJpaTest {
>
>        @Autowired
>        protected EntityManagerFactory emf;
>
>        @PersistenceContext
>        protected EntityManager em;
>
>        @PostConstruct
>        public void afterPropertiesSet() throws Exception {
>                emf.createEntityManager();
>        }
> }
> ========================
> package example.test.unit;
>
> import static org.junit.Assert.assertEquals;
>
> import org.junit.Test;
>
> import example.model.Concrete;
> import example.model.Discriminator;
>
> public class DiscriminatorTest {
>
>        @Test
>        public void testDiscriminator() throws ClassNotFoundException {
>                Discriminator d = Discriminator.CONCRETE;
>
>  assertEquals(Discriminator.getDiscriminatorName(Concrete.class), d
>                                .name());
>
>                assertEquals(Discriminator.getDiscriminatorClassFor(d.name
> ()),
>                                Concrete.class);
>        }
> }
> ===========================
> # log4j.properties
> log4j.rootLogger=ERROR, A1
> log4j.appender.A1=org.apache.log4j.ConsoleAppender
> log4j.appender.A1.layout=org.apache.log4j.PatternLayout
> log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss.SSS} [%t] %-5p
> %c %x - %m%n
>
> log4j.logger.example=debug
> ===========================
> # test.properties
> test.database=my-integration-test
> ===========================
> <!-- 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:aop="http://www.springframework.org/schema/aop"
>        xmlns:tx="http://www.springframework.org/schema/tx"
> xmlns:context="http://www.springframework.org/schema/context"
>        xsi:schemaLocation="http://www.springframework.org/schema/beans
> http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
>                           http://www.springframework.org/schema/aop
>  http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
>                           http://www.springframework.org/schema/tx
>  http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
>
> http://www.springframework.org/schema/context
> http://www.springframework.org/schema/context/spring-context-2.5.xsd">
>
>        <bean
>
>  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
>                <property name="location" value="classpath:test.properties"
> />
>        </bean>
>
>        <bean id="HsqldbDataSource" class="org.hsqldb.jdbc.jdbcDataSource">
>                <property name="user" value="sa" />
>                <property name="database"
> value="jdbc:hsqldb:mem:${test.database}" />
>        </bean>
>
>        <bean id="entityManagerFactory"
>
>  class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
>                <property name="persistenceUnitName" value="case-sandbox" />
>                <property name="jpaProperties">
>                        <value>
>                                openjpa.AutoDetach=commit
>                                openjpa.DetachState=fgs
>                                openjpa.QueryCache=false
>                                openjpa.jdbc.SubclassFetchMode=none
>                                openjpa.jdbc.EagerFetchMode=none
>
>  openjpa.jdbc.SynchronizeMappings=buildSchema(ForeignKeys=true)
>      </value>
>                </property>
>                <property name="jpaVendorAdapter">
>                        <bean
> class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter">
>                                <property name="showSql"
> value="${test.showSql}" />
>                                <property name="generateDdl" value="false"
> />
>                        </bean>
>                </property>
>                <property name="dataSource" ref="HsqldbDataSource" />
>        </bean>
>        <bean
>
>  class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"
> />
>
>        <bean id="transactionManager"
> class="org.springframework.orm.jpa.JpaTransactionManager">
>                <property name="entityManagerFactory"
> ref="entityManagerFactory" />
>                <property name="jpaDialect">
>                        <bean
> class="org.springframework.orm.jpa.vendor.OpenJpaDialect" />
>                </property>
>                <property name="dataSource" ref="HsqldbDataSource" />
>        </bean>
>        <tx:annotation-driven transaction-manager="transactionManager"
>                proxy-target-class="true" />
>
> </beans>
> =============================
> <!-- orm.xml -->
> <?xml version="1.0" encoding="UTF-8" ?>
> <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
>        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
>        http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
>        version="1.0">
>
>        <persistence-unit-metadata>
>                <persistence-unit-defaults>
>                        <access>FIELD</access>
>                        <cascade-persist/>
>                </persistence-unit-defaults>
>        </persistence-unit-metadata>
>
> </entity-mappings>
> ================================
> <!-- persistence.xml -->
> <?xml version="1.0"
>      encoding="UTF-8"?>
>
> <persistence xmlns="http://java.sun.com/xml/ns/persistence"
>        version="1.0">
>        <persistence-unit name="case-sandbox">
>                <provider>
>            org.apache.openjpa.persistence.PersistenceProviderImpl
>                </provider>
>        </persistence-unit>
> </persistence>
> ===============================
> <!-- pom.xml -->
> <?xml version="1.0"
>      encoding="UTF-8"?>
>
> <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>org.mytest</groupId>
>        <artifactId>case-sandbox</artifactId>
>        <packaging>jar</packaging>
>        <version>1.0.0-SNAPSHOT</version>
>        <name>Sandbox</name>
>        <properties>
>                <surefire.debug.suspend>n</surefire.debug.suspend>
>                <test.showSql>true</test.showSql>
>                <spring.version>2.5.6</spring.version>
>                <slf4j.version>1.5.6</slf4j.version>
>                <jpa.spec.version>1.0</jpa.spec.version>
>                <openjpa.version>1.2.1</openjpa.version>
>                <hsql.version>1.8.0.1</hsql.version>
>        </properties>
>        <dependencies>
>                <dependency>
>                        <groupId>org.slf4j</groupId>
>                        <artifactId>slf4j-log4j12</artifactId>
>                        <version>${slf4j.version}</version>
>                </dependency>
>                <dependency>
>                        <groupId>org.slf4j</groupId>
>                        <artifactId>slf4j-api</artifactId>
>                        <version>${slf4j.version}</version>
>                </dependency>
>                <dependency>
>                        <groupId>javax.persistence</groupId>
>                        <artifactId>persistence-api</artifactId>
>                        <version>${jpa.spec.version}</version>
>                </dependency>
>                <dependency>
>                        <groupId>org.apache.openjpa</groupId>
>                        <artifactId>openjpa</artifactId>
>                        <version>${openjpa.version}</version>
>                </dependency>
>                <dependency>
>                        <groupId>hsqldb</groupId>
>                        <artifactId>hsqldb</artifactId>
>                        <version>${hsql.version}</version>
>                </dependency>
>                <dependency>
>                        <groupId>org.springframework</groupId>
>                        <artifactId>spring</artifactId>
>                        <version>${spring.version}</version>
>                </dependency>
>                <dependency>
>                        <groupId>junit</groupId>
>                        <artifactId>junit</artifactId>
>                        <version>4.6</version>
>                </dependency>
>                <dependency>
>                        <groupId>org.springframework</groupId>
>                        <artifactId>spring-test</artifactId>
>                        <version>${spring.version}</version>
>                        <exclusions>
>                                <exclusion>
>                                        <groupId>junit</groupId>
>                                        <artifactId>junit</artifactId>
>                                </exclusion>
>                        </exclusions>
>                </dependency>
>                <dependency>
>                        <groupId>cglib</groupId>
>                        <artifactId>cglib-nodep</artifactId>
>                        <version>2.1_3</version>
>                </dependency>
>                <!--
>                <dependency>
>                        <groupId>org.jmock</groupId>
>                        <artifactId>jmock-junit4</artifactId>
>                        <scope>test</scope>
>                </dependency>
>                -->
>        </dependencies>
>        <build>
>                <testResources>
>                        <testResource>
>                                <filtering>true</filtering>
>                                <directory>src/test/resources</directory>
>                                <includes>
>                                        <include>**/*</include>
>                                </includes>
>                        </testResource>
>                </testResources>
>        <plugins>
>                <plugin>
>                        <groupId>org.apache.maven.plugins</groupId>
>                        <artifactId>maven-jar-plugin</artifactId>
>                        <version>2.2</version>
>                        <executions>
>                                <execution>
>                                        <goals>
>                                                <goal>test-jar</goal>
>                                        </goals>
>                                </execution>
>                        </executions>
>                </plugin>
>                        <plugin>
>                                <groupId>org.apache.maven.plugins</groupId>
>
>  <artifactId>maven-compiler-plugin</artifactId>
>                                <configuration>
>                                        <source>1.6</source>
>                                        <target>1.6</target>
>                                </configuration>
>                        </plugin>
>                        <plugin>
>                                <groupId>org.apache.maven.plugins</groupId>
>                                <artifactId>maven-antrun-plugin</artifactId>
>                                <version>1.1</version>
>                                <executions>
>                                        <execution>
>                                                <id>1</id>
>
>  <phase>process-classes</phase>
>                                                <configuration>
>                                                        <tasks>
>                                                                <path
> id="cp">
>
>  <path refid="maven.compile.classpath" />
>                                                                </path>
>
>                                                                <pathconvert
> pathsep=" " property="arguments"
>
>  dirsep=".">
>                                                                        <map
> from="${basedir}/target/classes/" to="" />
>                                                                        <map
> from="${basedir}\target\classes\" to="" />
>
>  <mapper>
>
>    <chainedmapper>
>
>            <globmapper from="*.class" to="*" />
>
>    </chainedmapper>
>
>  </mapper>
>
>  <path id="model.path">
>
>    <fileset dir="${basedir}/target/classes/">
>
>            <include name="**/*.class" />
>
>            <exclude name="**/annotations/**/*.class" />
>
>    </fileset>
>
>  </path>
>
>  </pathconvert>
>                                                                <java
> classname="org.apache.openjpa.enhance.PCEnhancer"
>
>  classpathref="cp" dir="target/classes" fork="true">
>                                                                        <arg
> line="${arguments} -p persistence.xml#case-sandbox" />
>                                                                </java>
>                                                        </tasks>
>                                                </configuration>
>                                                <goals>
>                                                        <goal>run</goal>
>                                                </goals>
>                                        </execution>
>                                        <execution>
>                                                <id>2</id>
>                                                <configuration>
>                                                        <tasks>
>                                                                <path
> id="cp">
>
>  <path refid="maven.compile.classpath" />
>                                                                </path>
>
>  <java classname="org.apache.openjpa.jdbc.meta.MappingTool"
>
>    classpathref="cp" dir="target/classes" fork="true">
>
>
>    <arg line="-p persistence.xml#case-sandbox" />
>
>    <arg line="-connectionDriverName org.hsqldb.jdbc.jdbcDataSource" />
>
>    <arg line="-connectionURL jdbc:hsqldb:mem:dummy" />
>
>    <arg line="-schemaAction build" />
>
>    <arg line="-sql ../test.sql" />
>
>  </java>
>
>                                                        </tasks>
>                                                </configuration>
>                                                <phase>compile</phase>
>                                                <goals>
>                                                        <goal>run</goal>
>                                                </goals>
>                                        </execution>
>
>                                </executions>
>                        </plugin>
>                        <plugin>
>                                <groupId>org.apache.maven.plugins</groupId>
>
>  <artifactId>maven-surefire-plugin</artifactId>
>                                <version>2.3</version>
>                                <configuration>
>
>  <argLine>-Xrunjdwp:transport=dt_socket,address=9998,server=y,suspend=${surefire.debug.suspend}
> -Xmx512m</argLine>
>                                        <includes>
>
>  <include>**/*Test.java</include>
>                                        </includes>
>                                        <excludes>
>
>  <exclude>**/integration/**</exclude>
>                                        </excludes>
>                                </configuration>
>                                <executions>
>                                        <execution>
>                                                <id>integration-tests</id>
>
>  <phase>integration-test</phase>
>                                                <goals>
>                                                        <goal>test</goal>
>                                                </goals>
>                                                <configuration>
>                                                        <skip>false</skip>
>                                                        <excludes>
>
>  <exclude>**/unit/**</exclude>
>                                                        </excludes>
>                                                        <includes>
>
>  <include>**/integration/**/*Test.java</include>
>                                                        </includes>
>                                                </configuration>
>                                        </execution>
>                                </executions>
>                        </plugin>
>                </plugins>
>        </build>
> </project>
> ================================
>
Matthew Adams

Re: Using mappedBy & getting "not a valid inverse relation"

Reply Threaded More More options
Print post
Permalink
Hi Mike,

<response-to-disclosure>The sample
(https://issues.apache.org/jira/secure/attachment/12423038/case-sandbox.zip)
can be unzipped and run immediately via maven (as well as imported
into eclipse).  Try it--it'll be fun!  :) </response-to-disclosure>

I tried your suggestion of going to property access (for both Concrete
& Pebble), which is very much **not** desirable IMHO, and moving the
@OneToMany annotation to BuildingMaterial's getPebbles() method.  I
got an interesting result.  The schema came out correctly with only
two tables, but I got a runtime error attempting to persist a new
Concrete:

<openjpa-1.2.1-r752877:753278 nonfatal store error>
org.apache.openjpa.persistence.EntityExistsException: Attempt to
persist detached object "example.model.Concrete@81fb".  If this is a
new instance, make sure any version and/or auto-generated primary key
fields are null/default when persisting.
FailedObject: example.model.Concrete@81fb

I've uploaded the property example to OPENJPA-1361
(https://issues.apache.org/jira/secure/attachment/12423454/case-sandbox-property-access.zip),
but property access is not something I want to move to.  Short of
defining two methods for each property (one to be called by OpenJPA,
one to be called by clients of the entity), I can't execute any
business logic in the setter methods.  That's the traditional argument
against property access, one to which I subscribe.

Thanks for thinking about the problem, though!

-matthew

On Tue, Oct 27, 2009 at 7:04 PM, Michael Dick <[hidden email]> wrote:

> Hi Matthew,
>
> <disclosure>I haven't tried the example you submitted. </disclosure>
>
> 1. If I had to guess I'd say that it's because the interface
> (BuildingMaterial) doesn't have the @OneToMany annotation, and as a result
> it's not a valid target for the relationship. Adding the annotatation (and
> switching to property access instead of field access) might help here.
>
> If you can't move to property access and add the annotation then I'm not
> sure how you could use an interface.. Using a MappedSuperclass for
> BuildingMaterial instead of an interface (could be an abstract
> MappedSuperclass though).
>
> 2. I'd have to look into @JoinColumn, I suspect it would result in similar
> behavior though (I'm prepared to be wrong here - haven't used JoinColumn
> much).
>
> Hope this helps
> -mike
>
> On Fri, Oct 23, 2009 at 12:54 PM, Matthew Adams <[hidden email]>wrote:
>
>> Hi all,
>>
>> NB:  All source for this example is attached and is pasted at the end
>> of this email, in case the attachment doesn't survive the mailing
>> list.  I've also entered the following JIRA to track this issue:
>> https://issues.apache.org/jira/browse/OPENJPA-1361
>>
>> Synopsis:  bidirectional one-to-many relationship where the one side
>> references the objects on the many side using a concrete class
>> reference and the many side references the object on the one side
>> using an interface reference.  When specifying the mappedBy property
>> of the @OneToMany annotation, OpenJPA throws the error:
>>
>> Collection field "example.model.Concrete.pebbles" declares that it is
>> mapped by "example.model.Pebble.buildingMaterial", but this is not a
>> valid inverse relation.
>>
>> When I remove the mappedBy property, everything works fine, except
>> that OpenJPA creates an extra join table (Concrete_Pebble) where it
>> shouldn't, IMHO.  I have two questions, an answer to either one of
>> which will suffice:
>>
>> 1. Why can't I use mappedBy in this scenario?
>> 2. How do I annotate things with @JoinColumn or similar that will
>> allow me to not have a join table and only the reference from Pebble
>> back to its BuildingMaterial?
>>
>> Thanks,
>> Matthew
>> ====================
>> package example.model;
>>
>> import java.util.HashSet;
>> import java.util.Set;
>>
>> import javax.persistence.CascadeType;
>> 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.OneToMany;
>> import javax.persistence.Version;
>>
>> @Entity
>> public class Concrete implements BuildingMaterial {
>>
>>        @Id
>>        @GeneratedValue(strategy = GenerationType.AUTO)
>>        @Column(name = "id")
>>        private Long assignedId;
>>
>>        @Version
>>        @Column(name = "version")
>>        private long assignedVersion;
>>
>>        @OneToMany(cascade = { CascadeType.MERGE, CascadeType.REFRESH,
>>                        CascadeType.PERSIST }, fetch = FetchType.LAZY)
>>        public Set<Pebble> pebbles = new HashSet<Pebble>();
>>
>>        public Long getId() {
>>                return assignedId;
>>        }
>>
>>        public long getVersion() {
>>                return assignedVersion;
>>        }
>>
>>        public Discriminator getDiscriminator() {
>>                return Discriminator.CONCRETE;
>>        }
>>
>>        public void add(Pebble pebble) {
>>                this.pebbles.add(pebble);
>>                pebble.buildingMaterial = this;
>>        }
>>
>>        public Set<Pebble> getPebbles() {
>>                return new HashSet<Pebble>(pebbles);
>>        }
>> }
>> ===========================
>> package example.model;
>>
>> import javax.persistence.CascadeType;
>> 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.ManyToOne;
>> import javax.persistence.Version;
>>
>> import org.apache.openjpa.persistence.jdbc.Strategy;
>>
>> @Entity
>> public class Pebble implements PersistentlyIdentifiable {
>>
>>        @Id
>>        @GeneratedValue(strategy = GenerationType.AUTO)
>>        @Column(name = "id")
>>        private Long assignedId;
>>
>>        @Version
>>        @Column(name = "version")
>>        private long assignedVersion;
>>
>>        @ManyToOne(cascade = { CascadeType.MERGE, CascadeType.REFRESH,
>>                        CascadeType.PERSIST }, fetch = FetchType.LAZY)
>>        @Strategy("example.openjpa.PersistentInterfaceValueHandler")
>>        protected BuildingMaterial buildingMaterial;
>>
>>        public Long getId() {
>>                return assignedId;
>>        }
>>
>>        public long getVersion() {
>>                return assignedVersion;
>>        }
>>
>>        public void setBuildingMaterial(BuildingMaterial buildingMaterial) {
>>                buildingMaterial.add(this);
>>        }
>>
>>        public Discriminator getDiscriminator() {
>>                return Discriminator.PEBBLE;
>>        }
>>
>>        public BuildingMaterial getBuildingMaterial() {
>>                return buildingMaterial;
>>        }
>> }
>> ============================
>> package example.model;
>>
>> public interface PersistentlyIdentifiable {
>>        Comparable<?> getId();
>>
>>        long getVersion();
>>
>>        Discriminator getDiscriminator();
>> }
>> ============================
>> package example.model;
>>
>> import java.util.Set;
>>
>> public interface BuildingMaterial extends PersistentlyIdentifiable {
>>
>>        void add(Pebble pebble);
>>
>>        Set<Pebble> getPebbles();
>> }
>> ============================
>> package example.model;
>>
>> import java.util.HashMap;
>> import java.util.Map;
>>
>> /**
>>  * Enum used to store discriminators in the database for polymorphic
>> references.
>>  */
>> public enum Discriminator {
>>
>>        CONCRETE(Concrete.class), PEBBLE(Pebble.class);
>>
>>        private static class X {
>>                public static final Map<String, Discriminator>
>> discriminatorsByClassName = new HashMap<String, Discriminator>();
>>
>>                public static void storeByClassName(Discriminator
>> discriminator) {
>>                        synchronized (discriminatorsByClassName) {
>>
>>  discriminatorsByClassName.put(discriminator.getClassName(),
>>                                                discriminator);
>>                        }
>>                }
>>        }
>>
>>        private Discriminator(Class<?> clazz) {
>>                this.className = clazz.getName();
>>                X.storeByClassName(this);
>>        }
>>
>>        private String className;
>>
>>        public String getClassName() {
>>                return className;
>>        }
>>
>>        public static Discriminator getDiscriminatorEnum(String className) {
>>                return X.discriminatorsByClassName.get(className);
>>        }
>>
>>        public static String getDiscriminatorName(Class<?> clazz) {
>>                Discriminator d = getDiscriminatorEnum(clazz.getName());
>>                if (d == null) {
>>                        throw new IllegalArgumentException("Class " +
>> clazz.getName()
>>                                        + " does not have a corresponding
>> Discriminator");
>>                }
>>                return d.name();
>>        }
>>
>>        public static Class<?> getDiscriminatorClassFor(String name)
>>                        throws ClassNotFoundException {
>>
>>                return getDiscriminatorClassFor(name, Discriminator.class
>>                                .getClassLoader());
>>        }
>>
>>        public static Class<?> getDiscriminatorClassFor(String name,
>>                        ClassLoader loader) throws ClassNotFoundException {
>>
>>                return
>> Class.forName(Discriminator.valueOf(name).getClassName(), true,
>>                                loader);
>>        }
>> }
>> ===========================
>> package example.openjpa;
>>
>> import java.sql.SQLException;
>>
>> import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
>> import org.apache.openjpa.jdbc.kernel.JDBCStore;
>> import org.apache.openjpa.jdbc.meta.ValueMapping;
>> import org.apache.openjpa.jdbc.meta.strats.AbstractValueHandler;
>> import org.apache.openjpa.jdbc.schema.Column;
>> import org.apache.openjpa.jdbc.schema.ColumnIO;
>> import org.apache.openjpa.kernel.OpenJPAStateManager;
>> import org.apache.openjpa.kernel.StoreContext;
>> import org.apache.openjpa.meta.JavaTypes;
>> import org.apache.openjpa.util.StoreException;
>> import org.slf4j.Logger;
>> import org.slf4j.LoggerFactory;
>>
>> import example.model.Discriminator;
>> import example.model.PersistentlyIdentifiable;
>>
>> @SuppressWarnings("serial")
>> public class PersistentInterfaceValueHandler extends AbstractValueHandler {
>>
>>        Logger log =
>> LoggerFactory.getLogger(PersistentInterfaceValueHandler.class);
>>
>>        @Override
>>        public Column[] map(ValueMapping valueMapping, String fieldName,
>>                        ColumnIO columnIo, boolean adapt) {
>>                Column discriminatorColumn = new Column();
>>                discriminatorColumn.setName(fieldName.toUpperCase() +
>> "_DISCRIMINATOR");
>>                discriminatorColumn.setJavaType(JavaTypes.STRING);
>>
>>                Column idColumn = new Column();
>>                idColumn.setName(fieldName.toUpperCase() + "_ID");
>>                idColumn.setJavaType(JavaTypes.LONG);
>>
>>                return new Column[] { discriminatorColumn, idColumn };
>>        }
>>
>>        public boolean isVersionable(ValueMapping vm) {
>>                return true;
>>        }
>>
>>        public boolean objectValueRequiresLoad(ValueMapping vm) {
>>                return true;
>>        }
>>
>>        public Object toDataStoreValue(ValueMapping vm, Object val,
>> JDBCStore store) {
>>                if (val == null) {
>>                        return new Object[] { null, null };
>>                }
>>                return new Object[] { deriveDiscriminator(val.getClass()),
>>                                ((PersistentlyIdentifiable) val).getId() };
>>        }
>>
>>        public String deriveDiscriminator(Class<?> clazz) {
>>                if (log.isDebugEnabled()) {
>>                        log.debug("Getting discriminator for class: [" +
>> clazz.getName()
>>                                        + "]");
>>                }
>>                String discriminator =
>> Discriminator.getDiscriminatorName(clazz);
>>                if (log.isDebugEnabled()) {
>>                        log.debug("Discriminator for class: [" +
>> clazz.getName() + "]:  ["
>>                                        + discriminator + "]");
>>                }
>>                return discriminator;
>>        }
>>
>>        public Class<?> deriveClass(String name, ClassLoader loader)
>>                        throws ClassNotFoundException {
>>                if (log.isDebugEnabled()) {
>>                        log.debug("Getting class for discriminator: [" +
>> name + "]");
>>                }
>>                Class<?> clazz =
>> Discriminator.getDiscriminatorClassFor(name, loader);
>>                if (log.isDebugEnabled()) {
>>                        log.debug("Class for discriminator: [" + name + "]:
>>  ["
>>                                        + clazz.getName() + "]");
>>                }
>>                return clazz;
>>        }
>>
>>        public Object toObjectValue(ValueMapping vm, Object val,
>>                        OpenJPAStateManager sm, JDBCStore store,
>>                        JDBCFetchConfiguration fetch) throws SQLException {
>>
>>                if (val == null) {
>>                        return null;
>>                }
>>                Object[] values = (Object[]) val;
>>                StoreContext ctx = store.getContext();
>>                ClassLoader loader = store.getConfiguration()
>>
>>  .getClassResolverInstance().getClassLoader(vm.getType(),
>>                                                ctx.getClassLoader());
>>                Class<?> cls = null;
>>                String clsName = (String) values[0];
>>                Long oidStr = (Long) values[1];
>>                if (clsName == null) {
>>                        return null;
>>                }
>>                try {
>>                        cls = deriveClass(clsName, loader);
>>                } catch (ClassNotFoundException cnfe) {
>>                        throw new StoreException(cnfe);
>>                }
>>                Object oid = ctx.newObjectId(cls, oidStr);
>>                return store.find(oid, vm, fetch);
>>        }
>> }
>> ========================
>> package example.test.integration;
>>
>> import static org.junit.Assert.assertEquals;
>> import static org.junit.Assert.assertNotNull;
>>
>> import java.util.Set;
>>
>> import org.junit.Test;
>> import org.springframework.transaction.annotation.Transactional;
>>
>> import example.model.BuildingMaterial;
>> import example.model.Concrete;
>> import example.model.Pebble;
>>
>> public class ConcretePebbleTest extends AbstractJpaTest {
>>
>>        @Test
>>        @Transactional
>>        public void testConcrete() {
>>
>>                BuildingMaterial bm = new Concrete();
>>                em.persist(bm);
>>
>>                Pebble p = new Pebble();
>>                bm.add(p);
>>
>>                em.flush();
>>
>>                Object bmid = bm.getId();
>>                Object pid = p.getId();
>>
>>                em.clear();
>>
>>                // navigate from BuildingMaterials side
>>                bm = em.find(bm.getClass(), bmid);
>>                Set<Pebble> pebbles = null;
>>                assertNotNull(pebbles = bm.getPebbles());
>>                assertEquals(1, pebbles.size());
>>
>>                em.clear();
>>
>>                // navigate from Pebble side
>>                p = em.find(Pebble.class, pid);
>>                assertNotNull(bm = p.getBuildingMaterial());
>>        }
>> }
>> =======================
>> package example.test.integration;
>>
>> import javax.annotation.PostConstruct;
>> import javax.persistence.EntityManager;
>> import javax.persistence.EntityManagerFactory;
>> import javax.persistence.PersistenceContext;
>>
>> import org.junit.runner.RunWith;
>> import org.springframework.beans.factory.annotation.Autowired;
>> import org.springframework.test.context.ContextConfiguration;
>> import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
>>
>> @ContextConfiguration(locations={"classpath:applicationContext.xml"})
>> @RunWith(SpringJUnit4ClassRunner.class)
>> abstract public class AbstractJpaTest {
>>
>>        @Autowired
>>        protected EntityManagerFactory emf;
>>
>>        @PersistenceContext
>>        protected EntityManager em;
>>
>>        @PostConstruct
>>        public void afterPropertiesSet() throws Exception {
>>                emf.createEntityManager();
>>        }
>> }
>> ========================
>> package example.test.unit;
>>
>> import static org.junit.Assert.assertEquals;
>>
>> import org.junit.Test;
>>
>> import example.model.Concrete;
>> import example.model.Discriminator;
>>
>> public class DiscriminatorTest {
>>
>>        @Test
>>        public void testDiscriminator() throws ClassNotFoundException {
>>                Discriminator d = Discriminator.CONCRETE;
>>
>>  assertEquals(Discriminator.getDiscriminatorName(Concrete.class), d
>>                                .name());
>>
>>                assertEquals(Discriminator.getDiscriminatorClassFor(d.name
>> ()),
>>                                Concrete.class);
>>        }
>> }
>> ===========================
>> # log4j.properties
>> log4j.rootLogger=ERROR, A1
>> log4j.appender.A1=org.apache.log4j.ConsoleAppender
>> log4j.appender.A1.layout=org.apache.log4j.PatternLayout
>> log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss.SSS} [%t] %-5p
>> %c %x - %m%n
>>
>> log4j.logger.example=debug
>> ===========================
>> # test.properties
>> test.database=my-integration-test
>> ===========================
>> <!-- 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:aop="http://www.springframework.org/schema/aop"
>>        xmlns:tx="http://www.springframework.org/schema/tx"
>> xmlns:context="http://www.springframework.org/schema/context"
>>        xsi:schemaLocation="http://www.springframework.org/schema/beans
>> http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
>>                           http://www.springframework.org/schema/aop
>>  http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
>>                           http://www.springframework.org/schema/tx
>>  http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
>>
>> http://www.springframework.org/schema/context
>> http://www.springframework.org/schema/context/spring-context-2.5.xsd">
>>
>>        <bean
>>
>>  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
>>                <property name="location" value="classpath:test.properties"
>> />
>>        </bean>
>>
>>        <bean id="HsqldbDataSource" class="org.hsqldb.jdbc.jdbcDataSource">
>>                <property name="user" value="sa" />
>>                <property name="database"
>> value="jdbc:hsqldb:mem:${test.database}" />
>>        </bean>
>>
>>        <bean id="entityManagerFactory"
>>
>>  class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
>>                <property name="persistenceUnitName" value="case-sandbox" />
>>                <property name="jpaProperties">
>>                        <value>
>>                                openjpa.AutoDetach=commit
>>                                openjpa.DetachState=fgs
>>                                openjpa.QueryCache=false
>>                                openjpa.jdbc.SubclassFetchMode=none
>>                                openjpa.jdbc.EagerFetchMode=none
>>
>>  openjpa.jdbc.SynchronizeMappings=buildSchema(ForeignKeys=true)
>>      </value>
>>                </property>
>>                <property name="jpaVendorAdapter">
>>                        <bean
>> class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter">
>>                                <property name="showSql"
>> value="${test.showSql}" />
>>                                <property name="generateDdl" value="false"
>> />
>>                        </bean>
>>                </property>
>>                <property name="dataSource" ref="HsqldbDataSource" />
>>        </bean>
>>        <bean
>>
>>  class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"
>> />
>>
>>        <bean id="transactionManager"
>> class="org.springframework.orm.jpa.JpaTransactionManager">
>>                <property name="entityManagerFactory"
>> ref="entityManagerFactory" />
>>                <property name="jpaDialect">
>>                        <bean
>> class="org.springframework.orm.jpa.vendor.OpenJpaDialect" />
>>                </property>
>>                <property name="dataSource" ref="HsqldbDataSource" />
>>        </bean>
>>        <tx:annotation-driven transaction-manager="transactionManager"
>>                proxy-target-class="true" />
>>
>> </beans>
>> =============================
>> <!-- orm.xml -->
>> <?xml version="1.0" encoding="UTF-8" ?>
>> <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
>>        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>>        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
>>        http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
>>        version="1.0">
>>
>>        <persistence-unit-metadata>
>>                <persistence-unit-defaults>
>>                        <access>FIELD</access>
>>                        <cascade-persist/>
>>                </persistence-unit-defaults>
>>        </persistence-unit-metadata>
>>
>> </entity-mappings>
>> ================================
>> <!-- persistence.xml -->
>> <?xml version="1.0"
>>      encoding="UTF-8"?>
>>
>> <persistence xmlns="http://java.sun.com/xml/ns/persistence"
>>        version="1.0">
>>        <persistence-unit name="case-sandbox">
>>                <provider>
>>            org.apache.openjpa.persistence.PersistenceProviderImpl
>>                </provider>
>>        </persistence-unit>
>> </persistence>
>> ===============================
>> <!-- pom.xml -->
>> <?xml version="1.0"
>>      encoding="UTF-8"?>
>>
>> <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>org.mytest</groupId>
>>        <artifactId>case-sandbox</artifactId>
>>        <packaging>jar</packaging>
>>        <version>1.0.0-SNAPSHOT</version>
>>        <name>Sandbox</name>
>>        <properties>
>>                <surefire.debug.suspend>n</surefire.debug.suspend>
>>                <test.showSql>true</test.showSql>
>>                <spring.version>2.5.6</spring.version>
>>                <slf4j.version>1.5.6</slf4j.version>
>>                <jpa.spec.version>1.0</jpa.spec.version>
>>                <openjpa.version>1.2.1</openjpa.version>
>>                <hsql.version>1.8.0.1</hsql.version>
>>        </properties>
>>        <dependencies>
>>                <dependency>
>>                        <groupId>org.slf4j</groupId>
>>                        <artifactId>slf4j-log4j12</artifactId>
>>                        <version>${slf4j.version}</version>
>>                </dependency>
>>                <dependency>
>>                        <groupId>org.slf4j</groupId>
>>                        <artifactId>slf4j-api</artifactId>
>>                        <version>${slf4j.version}</version>
>>                </dependency>
>>                <dependency>
>>                        <groupId>javax.persistence</groupId>
>>                        <artifactId>persistence-api</artifactId>
>>                        <version>${jpa.spec.version}</version>
>>                </dependency>
>>                <dependency>
>>                        <groupId>org.apache.openjpa</groupId>
>>                        <artifactId>openjpa</artifactId>
>>                        <version>${openjpa.version}</version>
>>                </dependency>
>>                <dependency>
>>                        <groupId>hsqldb</groupId>
>>                        <artifactId>hsqldb</artifactId>
>>                        <version>${hsql.version}</version>
>>                </dependency>
>>                <dependency>
>>                        <groupId>org.springframework</groupId>
>>                        <artifactId>spring</artifactId>
>>                        <version>${spring.version}</version>
>>                </dependency>
>>                <dependency>
>>                        <groupId>junit</groupId>
>>                        <artifactId>junit</artifactId>
>>                        <version>4.6</version>
>>                </dependency>
>>                <dependency>
>>                        <groupId>org.springframework</groupId>
>>                        <artifactId>spring-test</artifactId>
>>                        <version>${spring.version}</version>
>>                        <exclusions>
>>                                <exclusion>
>>                                        <groupId>junit</groupId>
>>                                        <artifactId>junit</artifactId>
>>                                </exclusion>
>>                        </exclusions>
>>                </dependency>
>>                <dependency>
>>                        <groupId>cglib</groupId>
>>                        <artifactId>cglib-nodep</artifactId>
>>                        <version>2.1_3</version>
>>                </dependency>
>>                <!--
>>                <dependency>
>>                        <groupId>org.jmock</groupId>
>>                        <artifactId>jmock-junit4</artifactId>
>>                        <scope>test</scope>
>>                </dependency>
>>                -->
>>        </dependencies>
>>        <build>
>>                <testResources>
>>                        <testResource>
>>                                <filtering>true</filtering>
>>                                <directory>src/test/resources</directory>
>>                                <includes>
>>                                        <include>**/*</include>
>>                                </includes>
>>                        </testResource>
>>                </testResources>
>>        <plugins>
>>                <plugin>
>>                        <groupId>org.apache.maven.plugins</groupId>
>>                        <artifactId>maven-jar-plugin</artifactId>
>>                        <version>2.2</version>
>>                        <executions>
>>                                <execution>
>>                                        <goals>
>>                                                <goal>test-jar</goal>
>>                                        </goals>
>>                                </execution>
>>                        </executions>
>>                </plugin>
>>                        <plugin>
>>                                <groupId>org.apache.maven.plugins</groupId>
>>
>>  <artifactId>maven-compiler-plugin</artifactId>
>>                                <configuration>
>>                                        <source>1.6</source>
>>                                        <target>1.6</target>
>>                                </configuration>
>>                        </plugin>
>>                        <plugin>
>>                                <groupId>org.apache.maven.plugins</groupId>
>>                                <artifactId>maven-antrun-plugin</artifactId>
>>                                <version>1.1</version>
>>                                <executions>
>>                                        <execution>
>>                                                <id>1</id>
>>
>>  <phase>process-classes</phase>
>>                                                <configuration>
>>                                                        <tasks>
>>                                                                <path
>> id="cp">
>>
>>  <path refid="maven.compile.classpath" />
>>                                                                </path>
>>
>>                                                                <pathconvert
>> pathsep=" " property="arguments"
>>
>>  dirsep=".">
>>                                                                        <map
>> from="${basedir}/target/classes/" to="" />
>>                                                                        <map
>> from="${basedir}\target\classes\" to="" />
>>
>>  <mapper>
>>
>>    <chainedmapper>
>>
>>            <globmapper from="*.class" to="*" />
>>
>>    </chainedmapper>
>>
>>  </mapper>
>>
>>  <path id="model.path">
>>
>>    <fileset dir="${basedir}/target/classes/">
>>
>>            <include name="**/*.class" />
>>
>>            <exclude name="**/annotations/**/*.class" />
>>
>>    </fileset>
>>
>>  </path>
>>
>>  </pathconvert>
>>                                                                <java
>> classname="org.apache.openjpa.enhance.PCEnhancer"
>>
>>  classpathref="cp" dir="target/classes" fork="true">
>>                                                                        <arg
>> line="${arguments} -p persistence.xml#case-sandbox" />
>>                                                                </java>
>>                                                        </tasks>
>>                                                </configuration>
>>                                                <goals>
>>                                                        <goal>run</goal>
>>                                                </goals>
>>                                        </execution>
>>                                        <execution>
>>                                                <id>2</id>
>>                                                <configuration>
>>                                                        <tasks>
>>                                                                <path
>> id="cp">
>>
>>  <path refid="maven.compile.classpath" />
>>                                                                </path>
>>
>>  <java classname="org.apache.openjpa.jdbc.meta.MappingTool"
>>
>>    classpathref="cp" dir="target/classes" fork="true">
>>
>>
>>    <arg line="-p persistence.xml#case-sandbox" />
>>
>>    <arg line="-connectionDriverName org.hsqldb.jdbc.jdbcDataSource" />
>>
>>    <arg line="-connectionURL jdbc:hsqldb:mem:dummy" />
>>
>>    <arg line="-schemaAction build" />
>>
>>    <arg line="-sql ../test.sql" />
>>
>>  </java>
>>
>>                                                        </tasks>
>>                                                </configuration>
>>                                                <phase>compile</phase>
>>                                                <goals>
>>                                                        <goal>run</goal>
>>                                                </goals>
>>                                        </execution>
>>
>>                                </executions>
>>                        </plugin>
>>                        <plugin>
>>                                <groupId>org.apache.maven.plugins</groupId>
>>
>>  <artifactId>maven-surefire-plugin</artifactId>
>>                                <version>2.3</version>
>>                                <configuration>
>>
>>  <argLine>-Xrunjdwp:transport=dt_socket,address=9998,server=y,suspend=${surefire.debug.suspend}
>> -Xmx512m</argLine>
>>                                        <includes>
>>
>>  <include>**/*Test.java</include>
>>                                        </includes>
>>                                        <excludes>
>>
>>  <exclude>**/integration/**</exclude>
>>                                        </excludes>
>>                                </configuration>
>>                                <executions>
>>                                        <execution>
>>                                                <id>integration-tests</id>
>>
>>  <phase>integration-test</phase>
>>                                                <goals>
>>                                                        <goal>test</goal>
>>                                                </goals>
>>                                                <configuration>
>>                                                        <skip>false</skip>
>>                                                        <excludes>
>>
>>  <exclude>**/unit/**</exclude>
>>                                                        </excludes>
>>                                                        <includes>
>>
>>  <include>**/integration/**/*Test.java</include>
>>                                                        </includes>
>>                                                </configuration>
>>                                        </execution>
>>                                </executions>
>>                        </plugin>
>>                </plugins>
>>        </build>
>> </project>
>> ================================
>>
>



--
mailto:[hidden email]
skype:matthewadams12
yahoo:matthewadams
aol:matthewadams12
google-talk:[hidden email]
msn:[hidden email]
http://matthewadams.me
http://www.linkedin.com/in/matthewadams