Sorry, this is a somewhat lengthy issue but I had several problems with unidirectional 1-M associations and Association Overrides. Eventually, I could find a workaround
but it looks to me as if there are some bugs and misfits to the JPA specification in OpenJPA. Here is the story:
I get following exception using Tomee 1.6:
<openjpa-2.3.0-nonfinal-1540826-r422266:1542644 fatal user error> org.apache.openjpa.persistence.ArgumentException: You have supplied columns for "com.logitags.cibet.actuator.archive.Archive.resource.com.logitags.cibet.core.Resource.parameters",
but this mapping cannot have columns in this context.
at org.apache.openjpa.jdbc.meta.MappingInfo.assertNoSchemaComponents(MappingInfo.java:382)
at org.apache.openjpa.jdbc.meta.strats.RelationToManyTableFieldStrategy.map(RelationToManyTableFieldStrategy.java:97)
at org.apache.openjpa.jdbc.meta.strats.RelationCollectionTableFieldStrategy.map(RelationCollectionTableFieldStrategy.java:94)
at org.apache.openjpa.jdbc.meta.FieldMapping.setStrategy(FieldMapping.java:146)
at org.apache.openjpa.jdbc.meta.RuntimeStrategyInstaller.installStrategy(RuntimeStrategyInstaller.java:82)
with following classes and persistence.xml
@Entity
public class Archive implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "CIB_SEQUENCE")
@TableGenerator(name = "CIB_SEQUENCE", table = "CIB_SEQUENCE", pkColumnName = "SEQUENCE", valueColumnName = "HI", pkColumnValue = "Archive", allocationSize = 1)
private long archiveId;
@Embedded
@AssociationOverride(name = "parameters", joinColumns = @JoinColumn(name = "archiveId", referencedColumnName = "archiveId"))
private Resource resource;
}
@Entity
public class DcControllable implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long dcControllableId;
@Embedded
@AssociationOverride(name = "parameters", joinColumns = @JoinColumn(name = "dcControllableId", referencedColumnName = "dcControllableId"))
private Resource resource;
}
@Embeddable
public class Resource implements Serializable, Cloneable {
@OneToMany(cascade ={ CascadeType.ALL }, fetch = FetchType.LAZY)
@JoinColumn(name = "dummy")
private List<ResourceParameter> parameters = new LinkedList<ResourceParameter>();
}
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="Cibet" transaction-type="JTA">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<jta-data-source>java:/CibetDS</jta-data-source>
<jar-file>../lib/cibet-${project.version}.jar</jar-file>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)" />
</properties>
</persistence-unit>
</persistence>
With annotations to use a join table it works. Join tables CIB_ARCHIVEPARAMETER and CIB_DCPARAMETER are created:
@Entity
public class Archive implements Serializable {
@Embedded
@AssociationOverride(name = "parameters", joinTable = @JoinTable(name = "CIB_ARCHIVEPARAMETER", joinColumns = @JoinColumn(name = "ARCHIVEID", referencedColumnName = "archiveId"), inverseJoinColumns = @JoinColumn(name = "PARAMETERID", referencedColumnName = "parameterId", unique = true)))
private Resource resource;
}
@Entity
public class DcControllable implements Serializable {
@Embedded
@AssociationOverride(name = "parameters", joinTable = @JoinTable(name = "CIB_DCPARAMETER", joinColumns = @JoinColumn(name = "DCCONTROLLABLEID", referencedColumnName = "dcControllableId"), inverseJoinColumns = @JoinColumn(name = "PARAMETERID", referencedColumnName = "parameterId", unique = true)))
private Resource resource;
}
@Embeddable
public class Resource implements Serializable, Cloneable {
@OneToMany(cascade ={ CascadeType.ALL }, fetch = FetchType.LAZY)
@JoinTable
private List<ResourceParameter> parameters = new LinkedList<ResourceParameter>();
}
However, the first solution using columns archiveId and
dcControllableId as foreign keys in ResourceParameter table without join table is working with Hibernate and EclipseLink, therefore I have to find a workaround for OpenJPA. The idea is to define the join table association in an orm.xml
that is only applied when using OpenJPA. As I want to replace the @JoinColumn with a @JoinTable in class Resource I have to use metadata-complete="true":
<?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_2_0.xsd"
version="2.0">
<description>Special mapping for OpenJPA because of bug in OpenJPA OneToMany with JoinColumn</description>
<entity class="com.logitags.cibet.actuator.archive.Archive" name="Archive">
<attributes>
<embedded name="resource">
<association-override name="parameters">
<join-table name="CIB_ARCHIVEPARAMETER">
<join-column name="ARCHIVEID" referenced-column-name="archiveId"/>
<inverse-join-column name="PARAMETERID" referenced-column-name="parameterId" unique="true"/>
</join-table>
</association-override>
</embedded>
</attributes>
</entity>
<entity class="com.logitags.cibet.actuator.dc.DcControllable" name="DcControllable">
<attributes>
<embedded name="resource">
<association-override name="parameters">
<join-table name="CIB_DCPARAMETER">
<join-column name="DCCONTROLLABLEID" referenced-column-name="dcControllableId"/>
<inverse-join-column name="PARAMETERID" referenced-column-name="parameterId" unique="true"/>
</join-table>
</association-override>
</embedded>
</attributes>
</entity>
<embeddable class="com.logitags.cibet.core.Resource" metadata-complete="true">
<attributes>
...
<one-to-many name="parameters" >
<join-table />
<cascade>
<cascade-all/>
</cascade>
</one-to-many>
</attributes>
</embeddable>
</entity-mappings>
This yields the same exception as above. It seems the association-overrides in orm.xml are ignored and the annotations are used again.
Therefore I use metadata-complete="true" also for Archive and DcControllable:
<?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_2_0.xsd"
version="2.0">
<description>Special mapping for OpenJPA because of bug in OpenJPA OneToMany with JoinColumn</description>
<entity class="com.logitags.cibet.actuator.archive.Archive" name="Archive" metadata-complete="true">
<table name="CIB_ARCHIVE"/>
<attributes>
...
<embedded name="resource">
<association-override name="parameters">
<join-table name="CIB_ARCHIVEPARAMETER">
<join-column name="ARCHIVEID" referenced-column-name="archiveId" />
<inverse-join-column name="PARAMETERID" referenced-column-name="parameterId" unique="true" />
</join-table>
</association-override>
</embedded>
</attributes>
</entity>
<entity class="com.logitags.cibet.actuator.dc.DcControllable" name="DcControllable" metadata-complete="true">
<table name="CIB_DCCONTROLLABLE"/>
<attributes>
...
<embedded name="resource">
<association-override name="parameters">
<join-table name="CIB_DCPARAMETER">
<join-column name="DCCONTROLLABLEID" referenced-column-name="dcControllableId" />
<inverse-join-column name="PARAMETERID" referenced-column-name="parameterId" unique="true" />
</join-table>
</association-override>
</embedded>
</attributes>
</entity>
<embeddable class="com.logitags.cibet.core.Resource" metadata-complete="true">
... same as above ...
</embeddable>
</entity-mappings>
At the end this works but not as expected: Instead of creating the join tables, columns archiveId and
dcControllableId in ResourceParameter table are used as foreign keys. Very strange! Does OpenJPA make an internal optimization?