Hi all,
The problem I'm having deals with a 0-N relation between two objects: Bill and PaymentAgreement. PaymentAgreement is an interface and is implemented by two classes AgreementX and AgreementY.
Because I want to be able to traverse all paymentAgreements for a bill, Bill contains a Set of paymentAgreement objects. PaymentAgreement and the two implementing classes are mapped as a "table-per-concrete-class" without implicit polymorphism, where PaymentAgreement itself is not mapped to a table because it is an interface and holds no data of its own. Because of this mapping, I'm forced to use union-subclasses each of which describing to which table they map and a many-to-one relation back to the Bill object.
During schema creation hbm2ddl reports duplicate column name BILL_ID. Hibernate probably refers the fact that both the Set containing the one-to-many mapping on the ONE-side, as the many-to-one mapping in the union-subclasses of the may side both refer to a column BILL_ID.
As far as I could make out from the docs, all (should) refer to one and the same foreign key in the mapped table which points back to the primary key of the BILL table.
I don't know how to fix this and any help would be appreciated very much.
Below is a small test program that reproduces the problem, please note that the code expects the following jar files in the classpath:
- hibernate-3.1.3.jar
- mysql-connector-java-3.1.13.jar
- dom4j-1.6.1.jar
- commons-logging-1.1.jar
- commons-collections-3.1.jar
- ehcache-1.1.jar
- cglib-2.1_3.jar
- asm-1.5.3.jar
- jta-1.0.1B.jar
Thanks in advance,
Raymond Brandon
Hibernate version: 3.1.3
Database version: MySQL 5.0 and mysql-connector-java-3.1.13.jar
Mapping Bill
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-lazy="true" package="model">
<class name="Bill" table="BILL">
<id name="id" column="BILL_ID" unsaved-value="null">
<generator class="increment" />
</id>
<property name="amount" column="AMOUNT" not-null="true" />
<set name="paymentAgreements" inverse="true">
<key column="BILL_ID"/>
<one-to-many class="PaymentAgreement" />
</set>
</class>
</hibernate-mapping>
Mapping PaymentAgreement
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="model">
<class name="PaymentAgreement" abstract="true">
<!-- The id property is inherited by the sub-classes and does not need to be repeated -->
<id name="id" type="long" column="PAYMENTAGREEMENT_ID" unsaved-value="null">
<generator class="increment" />
</id>
<union-subclass name="AgreementX" table="AGREEMENTX">
<property name="type" column="AGREEMENT_TYPE" />
<many-to-one name="bill"
column="BILL_ID"
class="Bill"
not-null="true"/>
</union-subclass>
<union-subclass name="AgreementY" table="AGREEMENTY">
<property name="type" column="AGREEMENT_TYPE" />
<many-to-one name="bill"
column="BILL_ID"
class="Bill"
not-null="true"/>
</union-subclass>
</class>
</hibernate-mapping>
Program code:
Code:
import model.Bill;
import model.PaymentAgreement;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class RunMe {
private static final String databasename = "defactodb";
public static void main(String[] args) {
Configuration cfg = new Configuration();
cfg.addClass(Bill.class);
cfg.addClass(PaymentAgreement.class);
cfg.setProperty("hibernate.connection.username", "test");
cfg.setProperty("hibernate.connection.password", "test");
cfg.setProperty("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
cfg.setProperty("hibernate.connection.url", "jdbc:mysql://localhost/" + databasename + "?useUnicode=true&characterEncoding=utf-8");
cfg.setProperty("hibernate.show_sql", "true");
cfg.setProperty("hibernate.hbm2ddl.auto", "create-drop");
cfg.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect");
SessionFactory sessions = cfg.buildSessionFactory();
Session session = sessions.openSession();
Bill bill = new Bill();
bill.setAmount(100.00);
session.save(bill);
}
}
package model;
import java.util.HashSet;
import java.util.Set;
public class Bill {
private Long id;
private double amount;
private Set paymentAgreements;
public Bill() {
paymentAgreements = new HashSet();
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Set getPaymentAgreements() {
return paymentAgreements;
}
public void setPaymentAgreements(Set paymentAgreements) {
this.paymentAgreements = paymentAgreements;
}
}
package model;
public interface PaymentAgreement {
public Long getId();
public void setId(Long id);
}
package model;
public class AgreementX implements PaymentAgreement {
private Long id;
private String type;
private Bill bill;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Bill getBill() {
return bill;
}
public void setBill(Bill bill) {
this.bill = bill;
}
}
package model;
public class AgreementY implements PaymentAgreement {
private Long id;
private String type;
private Bill bill;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Bill getBill() {
return bill;
}
public void setBill(Bill bill) {
this.bill = bill;
}
}
Debug level Hibernate log excerpt:
13-jul-2006 11:21:49 org.hibernate.cfg.Environment <clinit>
INFO: Hibernate 3.1.3
13-jul-2006 11:21:49 org.hibernate.cfg.Environment <clinit>
INFO: hibernate.properties not found
13-jul-2006 11:21:49 org.hibernate.cfg.Environment <clinit>
INFO: using CGLIB reflection optimizer
13-jul-2006 11:21:49 org.hibernate.cfg.Environment <clinit>
INFO: using JDK 1.4 java.sql.Timestamp handling
13-jul-2006 11:21:49 org.hibernate.cfg.Configuration addClass
INFO: Reading mappings from resource: model/Bill.hbm.xml
13-jul-2006 11:21:49 org.hibernate.cfg.HbmBinder bindRootPersistentClassCommonValues
INFO: Mapping class: model.Bill -> BILL
13-jul-2006 11:21:49 org.hibernate.cfg.Configuration addClass
INFO: Reading mappings from resource: model/PaymentAgreement.hbm.xml
13-jul-2006 11:21:49 org.hibernate.cfg.HbmBinder bindRootPersistentClassCommonValues
INFO: Mapping class: model.PaymentAgreement -> PaymentAgreement
13-jul-2006 11:21:49 org.hibernate.cfg.HbmBinder bindUnionSubclass
INFO: Mapping union-subclass: model.AgreementX -> AGREEMENTX
13-jul-2006 11:21:49 org.hibernate.cfg.HbmBinder bindUnionSubclass
INFO: Mapping union-subclass: model.AgreementY -> AGREEMENTY
13-jul-2006 11:21:49 org.hibernate.cfg.HbmBinder bindCollectionSecondPass
INFO: Mapping collection: model.Bill.paymentAgreements -> PaymentAgreement
13-jul-2006 11:21:49 org.hibernate.connection.DriverManagerConnectionProvider configure
INFO: Using Hibernate built-in connection pool (not for production use!)
13-jul-2006 11:21:49 org.hibernate.connection.DriverManagerConnectionProvider configure
INFO: Hibernate connection pool size: 20
13-jul-2006 11:21:49 org.hibernate.connection.DriverManagerConnectionProvider configure
INFO: autocommit mode: false
13-jul-2006 11:21:49 org.hibernate.connection.DriverManagerConnectionProvider configure
INFO: using driver: com.mysql.jdbc.Driver at URL: jdbc:mysql://localhost/defactodb?useUnicode=true&characterEncoding=utf-8
13-jul-2006 11:21:49 org.hibernate.connection.DriverManagerConnectionProvider configure
INFO: connection properties: {user=test, password=****}
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: RDBMS: MySQL, version: 5.0.21-community-nt
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: JDBC driver: MySQL-AB JDBC Driver, version: mysql-connector-java-3.1.13 ( $Date: 2005-11-17 15:53:48 +0100 (Thu, 17 Nov 2005) $, $Revision$ )
13-jul-2006 11:21:49 org.hibernate.dialect.Dialect <init>
INFO: Using dialect: org.hibernate.dialect.MySQLInnoDBDialect
13-jul-2006 11:21:49 org.hibernate.transaction.TransactionFactoryFactory buildTransactionFactory
INFO: Using default transaction strategy (direct JDBC transactions)
13-jul-2006 11:21:49 org.hibernate.transaction.TransactionManagerLookupFactory getTransactionManagerLookup
INFO: No TransactionManagerLookup configured (in JTA environment, use of read-write or transactional second-level cache is not recommended)
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Automatic flush during beforeCompletion(): disabled
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Automatic session close at end of transaction: disabled
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: JDBC batch size: 15
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: JDBC batch updates for versioned data: disabled
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Scrollable result sets: enabled
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: JDBC3 getGeneratedKeys(): enabled
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Connection release mode: auto
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Maximum outer join fetch depth: 2
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Default batch fetch size: 1
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Generate SQL with comments: disabled
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Order SQL updates by primary key: disabled
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory createQueryTranslatorFactory
INFO: Query translator: org.hibernate.hql.ast.ASTQueryTranslatorFactory
13-jul-2006 11:21:49 org.hibernate.hql.ast.ASTQueryTranslatorFactory <init>
INFO: Using ASTQueryTranslatorFactory
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Query language substitutions: {}
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Second-level cache: enabled
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Query cache: disabled
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory createCacheProvider
INFO: Cache provider: org.hibernate.cache.EhCacheProvider
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Optimize cache for minimal puts: disabled
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Structured second-level cache entries: disabled
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Echoing all SQL to stdout
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Statistics: disabled
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Deleted entity synthetic identifier rollback: disabled
13-jul-2006 11:21:49 org.hibernate.cfg.SettingsFactory buildSettings
INFO: Default entity-mode: pojo
13-jul-2006 11:21:49 org.hibernate.impl.SessionFactoryImpl <init>
INFO: building session factory
13-jul-2006 11:21:49 net.sf.ehcache.config.Configurator configure
WARNING: No configuration found. Configuring ehcache from ehcache-failsafe.xml found in the classpath: jar:file:/C:/Documents%20and%20Settings/Raymondb/.m2/repository/ehcache/ehcache/1.1/ehcache-1.1.jar!/ehcache-failsafe.xml
13-jul-2006 11:21:50 org.hibernate.impl.SessionFactoryObjectFactory addInstance
INFO: Not binding factory to JNDI, no JNDI name configured
13-jul-2006 11:21:50 org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: Running hbm2ddl schema export
13-jul-2006 11:21:50 org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: exporting generated schema to database
13-jul-2006 11:21:50 org.hibernate.tool.hbm2ddl.SchemaExport create
SEVERE: Unsuccessful: create table AGREEMENTX (PAYMENTAGREEMENT_ID bigint not null, BILL_ID bigint, AGREEMENT_TYPE varchar(255), BILL_ID bigint not null, primary key (PAYMENTAGREEMENT_ID)) type=InnoDB
13-jul-2006 11:21:50 org.hibernate.tool.hbm2ddl.SchemaExport create
SEVERE: Duplicate column name 'BILL_ID'
13-jul-2006 11:21:50 org.hibernate.tool.hbm2ddl.SchemaExport create
SEVERE: Unsuccessful: create table AGREEMENTY (PAYMENTAGREEMENT_ID bigint not null, BILL_ID bigint, AGREEMENT_TYPE varchar(255), BILL_ID bigint not null, primary key (PAYMENTAGREEMENT_ID)) type=InnoDB
13-jul-2006 11:21:50 org.hibernate.tool.hbm2ddl.SchemaExport create
SEVERE: Duplicate column name 'BILL_ID'
13-jul-2006 11:21:50 org.hibernate.tool.hbm2ddl.SchemaExport create
SEVERE: Unsuccessful: alter table AGREEMENTX add index FK3F946AC47423641F27eadbae (BILL_ID), add constraint FK3F946AC47423641F27eadbae foreign key (BILL_ID) references BILL (BILL_ID)
13-jul-2006 11:21:50 org.hibernate.tool.hbm2ddl.SchemaExport create
SEVERE: Table 'defactodb.agreementx' doesn't exist
13-jul-2006 11:21:50 org.hibernate.tool.hbm2ddl.SchemaExport create
SEVERE: Unsuccessful: alter table AGREEMENTY add index FK3F946AC47423641F27eadbaf (BILL_ID), add constraint FK3F946AC47423641F27eadbaf foreign key (BILL_ID) references BILL (BILL_ID)
13-jul-2006 11:21:50 org.hibernate.tool.hbm2ddl.SchemaExport create
SEVERE: Table 'defactodb.agreementy' doesn't exist
13-jul-2006 11:21:50 org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: schema export complete
Hibernate: select max(BILL_ID) from BILL