I'm using hibernate 2.1.1 under weblogic 8 with oracle 9.
I am seeing some strange behaviour with many-to-many mappings using composite-element. An unneccessary delete and insert statement is being sent to the database.
Tables:
PRODUCT
ID NUMBER(6) NOT NULL,
DESCRIPTION VARCHAR2(40 BYTE)
USER_ORDER
ID NUMBER(6) NOT NULL,
TOTAL NUMBER(6)
PRODUCT_USER_ORDER_MAP
PRODUCT_ID NUMBER(6) NOT NULL,
USER_ORDER_ID NUMBER(6) NOT NULL,
QUANTITY NUMBER(6)
OTHER
ID NUMBER(6) NOT NULL,
VALUE NUMBER(6)
Mapping documents:
Product
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="test.Product" table="PRODUCT" dynamic-insert="true" dynamic-update="true">
<id name="id" unsaved-value="null" column="ID">
<generator class="seqhilo">
<param name="sequence">SEQ_HIBERNATE_TEST</param>
<param name="max_lo">100</param>
</generator>
</id>
<property name="description" column="DESCRIPTION"/>
<set name="productUserOrderMap" table="PRODUCT_USER_ORDER_MAP" lazy="true">
<key column="PRODUCT_ID"/>
<composite-element class="test.ProductUserOrderMap">
<property name="quantity" column="QUANTITY"/>
<many-to-one name="userOrder" class="test.UserOrder" column="USER_ORDER_ID"/>
</composite-element>
</set>
</class>
</hibernate-mapping>
UserOrderCode:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="test.UserOrder" table="USER_ORDER" dynamic-insert="true" dynamic-update="true">
<id name="id" unsaved-value="null" column="ID">
<generator class="seqhilo">
<param name="sequence">SEQ_HIBERNATE_TEST</param>
<param name="max_lo">100</param>
</generator>
</id>
<property name="total" column="TOTAL"/>
<set name="productUserOrderMap" table="PRODUCT_USER_ORDER_MAP" lazy="true">
<key column="USER_ORDER_ID"/>
<composite-element class="test.ProductUserOrderMap">
<property name="quantity" column="QUANTITY"/>
<many-to-one name="product" class="test.Product" column="PRODUCT_ID"/>
</composite-element>
</set>
</class>
</hibernate-mapping>
OtherCode:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="test.Other" table="OTHER" dynamic-insert="true" dynamic-update="true">
<id name="id" unsaved-value="null" column="ID">
<generator class="seqhilo">
<param name="sequence">SEQ_HIBERNATE_TEST</param>
<param name="max_lo">100</param>
</generator>
</id>
<property name="value" column="VALUE"/>
</class>
</hibernate-mapping>
Hibernate Config XMLCode:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.datasource">oraclePool</property>
<property name="show_sql">true</property>
<property name="dialect">net.sf.hibernate.dialect.Oracle9Dialect</property>
<property name="transaction.factory_class">net.sf.hibernate.transaction.JTATransactionFactory</property>
<property name="transaction.manager_lookup_class">net.sf.hibernate.transaction.WeblogicTransactionManagerLookup</property>
<mapping resource="test/Product.hbm.xml"/>
<mapping resource="test/UserOrder.hbm.xml"/>
<mapping resource="test/Other.hbm.xml"/>
</session-factory>
</hibernate-configuration>
POJOs:ProductCode:
package test;
import java.util.Set;
public class Product {
private Integer id;
private String description;
private Set productUserOrderMap;
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Set getProductUserOrderMap() { return productUserOrderMap; }
public void setProductUserOrderMap(Set productUserOrderMap) { this.productUserOrderMap = productUserOrderMap; }
}
UserOrderCode:
package test;
import java.util.Set;
public class UserOrder {
private Integer id;
private Integer total;
private Set productUserOrderMap;
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public Integer getTotal() { return total; }
public void setTotal(Integer total) { this.total = total; }
public Set getProductUserOrderMap() { return productUserOrderMap; }
public void setProductUserOrderMap(Set productUserOrderMap) { this.productUserOrderMap = productUserOrderMap; }
}
ProductUserOrderMapCode:
package test;
public class ProductUserOrderMap {
private Product product;
private UserOrder userOrder;
private Integer quantity;
public Product getProduct() { return product; }
public void setProduct(Product product) { this.product = product; }
public UserOrder getUserOrder() { return userOrder; }
public void setUserOrder(UserOrder userOrder) { this.userOrder = userOrder; }
public Integer getQuantity() { return quantity; }
public void setQuantity(Integer quantity) { this.quantity = quantity; }
}
OtherCode:
package test;
public class Other {
private Integer id;
private Integer value;
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public Integer getValue() { return value; }
public void setValue(Integer value) { this.value = value; }
}
Client code (method in a stateless session bean):Code:
public void productUserOrderTest() throws Exception {
net.sf.hibernate.cfg.Configuration cfg = new net.sf.hibernate.cfg.Configuration();
cfg.configure();
net.sf.hibernate.SessionFactory sessionFactory = cfg.buildSessionFactory();
net.sf.hibernate.Session session = sessionFactory.openSession();
test.Product product = null;
try {
net.sf.hibernate.Criteria criteria = session.createCriteria(test.Product.class);
criteria.add(net.sf.hibernate.expression.Expression.eq("id", new Integer(1)));
criteria.setFetchMode("productUserOrderMap", net.sf.hibernate.FetchMode.EAGER);
List list = criteria.list();
product = (test.Product) list.get(0);
test.Other other = new test.Other();
other.setValue(new Integer(100));
session.save(other);
session.flush();
} catch (HibernateException ex) {
sessionContext_.setRollbackOnly();
logger_.error(ex);
throw new ClasswellRuntimeException(ex);
} finally {
try {
session.close();
} catch (HibernateException ex) {
logger_.error(ex);
throw new ClasswellRuntimeException(ex);
}
}
}
Log file output:Code:
- Hibernate 2.1.1
- hibernate.properties not found
- using CGLIB reflection optimizer
- configuring from resource: /hibernate.cfg.xml
- Configuration resource: /hibernate.cfg.xml
- Mapping resource: test/Product.hbm.xml
- Mapping class: test.Product -> PRODUCT
- Mapping collection: test.Product.productUserOrderMap -> PRODUCT_USER_ORDER_MAP
- Mapping resource: test/UserOrder.hbm.xml
- Mapping class: test.UserOrder -> USER_ORDER
- Mapping collection: test.UserOrder.productUserOrderMap -> PRODUCT_USER_ORDER_MAP
- Mapping resource: test/Other.hbm.xml
- Mapping class: test.Other -> OTHER
- Configured SessionFactory: null
- processing one-to-many association mappings
- processing one-to-one association property references
- processing foreign key constraints
- Using dialect: net.sf.hibernate.dialect.Oracle9Dialect
- Use outer join fetching: true
- JNDI InitialContext properties:{}
- Using datasource: classwellOraclePool
- Transaction strategy: net.sf.hibernate.transaction.JTATransactionFactory
- JNDI InitialContext properties:{}
- instantiating TransactionManagerLookup: net.sf.hibernate.transaction.WeblogicTransactionManagerLookup
- instantiated TransactionManagerLookup
- JNDI InitialContext properties:{}
- instantiating TransactionManagerLookup: net.sf.hibernate.transaction.WeblogicTransactionManagerLookup
- instantiated TransactionManagerLookup
- Use scrollable result sets: true
- JDBC 2 max batch size: 15
- echoing all SQL to stdout
- Query language substitutions: {}
- cache provider: net.sf.ehcache.hibernate.Provider
- instantiating and configuring caches
- building session factory
- no JNDI name configured
- JNDI InitialContext properties:{}
Hibernate: select this.ID as ID1_, this.DESCRIPTION as DESCRIPT2_1_, productuse1_.QUANTITY as QUANTITY__, productuse1_.USER_ORDER_ID as USER_ORD3___, productuse1_.PRODUCT_ID as PRODUCT_ID__, userorder2_.ID as ID0_, userorder2_.TOTAL as TOTAL0_ from PRODUCT this left outer join PRODUCT_USER_ORDER_MAP productuse1_ on this.ID=productuse1_.PRODUCT_ID left outer join USER_ORDER userorder2_ on productuse1_.USER_ORDER_ID=userorder2_.ID where this.ID=?
Hibernate: select SEQ_HIBERNATE_TEST.nextval from dual
Hibernate: insert into OTHER (VALUE, ID) values (?, ?)
Hibernate: delete from PRODUCT_USER_ORDER_MAP where PRODUCT_ID=? and QUANTITY=? and USER_ORDER_ID=?
Hibernate: insert into PRODUCT_USER_ORDER_MAP (PRODUCT_ID, QUANTITY, USER_ORDER_ID) values (?, ?, ?)
Note the last two lines of the log file delete and insert into the many-to-many mapping table even though no such data has been manipulated. Any ideas?
thanks,
-shreyank