Is it possible to create an array with polymorphic elements within hibernate, using a <many-to-any> tag inside an <array> mapping? In other words, is it possible to persist an entity that includes a field that looks as follows:
private Object[] objectArray;
I am using the basic org.hibernate.auction example that is supplied with the hibernate libraries to test this mapping. I added a class called StringEntity.java and changed the User.hbm.xml mapping file to include the array.
The <array> mapping allows one to include <many-to-any> tags, but when I run my code, I get a NullPointerException at java.lang.reflect.Array.newArray(Native Method).
I stepped through the code and noticed in net.sf.hibernate.cfg.Binder that the method bindArray (line 743) only checks for "element", "one-to-many", "many-to-many" and "composite-element" tags when assigning the ElementClass attribute for the array Collection. No valid tag is found, and I suspect that no ElementClass is set for the net.sf.hibernate.mapping.Array class, which seems to cause the NullPointerException. Does this mean that "many-to-any" is not supported for <array> in the mapping files?
If this is not supported, is there any other way in which I can map such an object array?
Hibernate version: 2.1.8
Mapping documents:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping
package="org.hibernate.auction">
<class name="User" table="AuctionUser" proxy="User">
<id name="id" column="id" type="long">
<generator class="native"/>
</id>
<property name="userName" not-null="true"/>
<property name="password" column="`password`"/>
<property name="email"/>
<component name="name">
<property name="firstName"/>
<property name="initial" column="`initial`"/>
<property name="lastName"/>
</component>
<bag name="bids" lazy="true" inverse="true" cascade="save-update">
<key column="bidder"/>
<one-to-many class="Bid"/>
</bag>
<bag name="auctions" lazy="true" inverse="true" cascade="save-update">
<key column="seller"/>
<one-to-many class="AuctionItem"/>
</bag>
<array
name="objectArray"
table="objectarray"
cascade="all" >
<key column="id" />
<index column="object_id" type="long" />
<many-to-any id-type="long" meta-type="string" >
<meta-value value="XXX" class="com.entelect.hibernate.entity.StringEntity" />
<meta-value value="N1" class="Name" />
<meta-value value="B1" class="Bid" />
<column name="class_type" />
<column name="class_id" />
</many-to-any>
</array>
</class>
</hibernate-mapping>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping
package="com.entelect.hibernate.entity"
schema="entity"
auto-import="false">
<class
name="StringEntity"
table="STRING_ENTITY">
<id
name="id"
column="STRING_ENTITY_ID"
type="long"
access="property">
<generator class="native"/>
</id>
<property
name="theString"
column="STRING"
type="string"
access="property">
</property>
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():
Session s = factory.openSession();
Transaction tx=null;
try {
tx = s.beginTransaction();
}
catch (Exception e) {
if (tx!=null) tx.rollback();
throw e;
}
//Transaction tx = s.beginTransaction();
User seller = new User();
seller.setUserName("oldirty");
seller.setName( new Name("ol' dirty", null, "bastard") );
seller.setEmail("[email protected]");
seller.setAuctions( new ArrayList() );
Object[] x = new Object[2];
x[0] = new StringEntity("XXX one");
x[1] = new StringEntity("XXX two");
for (int i=0; i<x.length; i++) {
System.out.println("x[" + i + "] = " + x[i].toString());
}
seller.setObjectArray(x);
s.save(seller);
tx.commit();
s.close();
package com.entelect.hibernate.entity;
public class StringEntity {
private Long id;
private String theString;
public StringEntity () {}
public StringEntity (String theString) {
this.theString = theString;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTheString() {
return theString;
}
public void setTheString(String theString) {
this.theString = theString;
}
}
//$Id: User.java,v 1.3 2004/06/04 01:27:33 steveebersole Exp $
package org.hibernate.auction;
import java.util.List;
/**
* @author Gavin King
*/
public class User extends Persistent {
private String userName;
private String password;
private String email;
private Name name;
private List bids;
private List auctions;
private Object[] objectArray;
public String getEmail() {
return email;
}
public String getPassword() {
return password;
}
public String getUserName() {
return userName;
}
public void setEmail(String string) {
email = string;
}
public void setPassword(String string) {
password = string;
}
public void setUserName(String string) {
userName = string;
}
public List getAuctions() {
return auctions;
}
public List getBids() {
return bids;
}
public void setAuctions(List list) {
auctions = list;
}
public void setBids(List list) {
bids = list;
}
public String toString() {
return userName;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
public void setObjectArray(Object[] objectArray) {
this.objectArray = objectArray;
}
public Object[] getObjectArray() {
return objectArray;
}
}
Full stack trace of any exception that occurs:
0 [main] INFO org.hibernate.auction.Main - org.apache.commons.logging.impl.Log4JLogger@c9ba38
50 [main] INFO net.sf.hibernate.cfg.Environment - Hibernate 2.1.8
50 [main] INFO net.sf.hibernate.cfg.Environment - hibernate.properties not found
60 [main] INFO net.sf.hibernate.cfg.Environment - using CGLIB reflection optimizer
60 [main] INFO net.sf.hibernate.cfg.Environment - using JDK 1.4 java.sql.Timestamp handling
80 [main] INFO net.sf.hibernate.cfg.Configuration - Mapping resource: org/hibernate/auction/AuctionItem.hbm.xml
781 [main] INFO net.sf.hibernate.cfg.Binder - Mapping class: org.hibernate.auction.AuctionItem -> AuctionItem
911 [main] INFO net.sf.hibernate.cfg.Configuration - Mapping resource: org/hibernate/auction/Bid.hbm.xml
951 [main] INFO net.sf.hibernate.cfg.Binder - Mapping class: org.hibernate.auction.Bid -> Bid
1001 [main] INFO net.sf.hibernate.cfg.Binder - Mapping subclass: org.hibernate.auction.BuyNow -> Bid
1001 [main] INFO net.sf.hibernate.cfg.Configuration - Mapping resource: org/hibernate/auction/User.hbm.xml
1071 [main] INFO net.sf.hibernate.cfg.Binder - Mapping class: org.hibernate.auction.User -> AuctionUser
1302 [main] INFO net.sf.hibernate.cfg.Binder - Mapping collection: org.hibernate.auction.User.objectArray -> objectarray
1312 [main] ERROR net.sf.hibernate.cfg.Configuration - Could not configure datastore from input stream
java.lang.NullPointerException
at java.lang.reflect.Array.newArray(Native Method)
at java.lang.reflect.Array.newInstance(Unknown Source)
at net.sf.hibernate.type.ArrayType.<init>(ArrayType.java:33)
at net.sf.hibernate.type.TypeFactory.array(TypeFactory.java:183)
at net.sf.hibernate.mapping.Array.getCollectionType(Array.java:29)
at net.sf.hibernate.mapping.Collection.getType(Collection.java:275)
at net.sf.hibernate.cfg.Binder.bindProperty(Binder.java:454)
at net.sf.hibernate.cfg.Binder.createProperty(Binder.java:1081)
at net.sf.hibernate.cfg.Binder.propertiesFromXML(Binder.java:1061)
at net.sf.hibernate.cfg.Binder.bindRootClass(Binder.java:362)
at net.sf.hibernate.cfg.Binder.bindRoot(Binder.java:1256)
at net.sf.hibernate.cfg.Configuration.add(Configuration.java:253)
at net.sf.hibernate.cfg.Configuration.addInputStream(Configuration.java:289)
at net.sf.hibernate.cfg.Configuration.addClass(Configuration.java:355)
at org.hibernate.auction.Main.main(Main.java:466)
net.sf.hibernate.MappingException: Error reading resource: org/hibernate/auction/User.hbm.xml
at net.sf.hibernate.cfg.Configuration.addClass(Configuration.java:358)
at org.hibernate.auction.Main.main(Main.java:466)
Caused by: net.sf.hibernate.MappingException: java.lang.NullPointerException
at net.sf.hibernate.cfg.Configuration.addInputStream(Configuration.java:297)
at net.sf.hibernate.cfg.Configuration.addClass(Configuration.java:355)
... 1 more
Caused by: java.lang.NullPointerException
at java.lang.reflect.Array.newArray(Native Method)
at java.lang.reflect.Array.newInstance(Unknown Source)
at net.sf.hibernate.type.ArrayType.<init>(ArrayType.java:33)
at net.sf.hibernate.type.TypeFactory.array(TypeFactory.java:183)
at net.sf.hibernate.mapping.Array.getCollectionType(Array.java:29)
at net.sf.hibernate.mapping.Collection.getType(Collection.java:275)
at net.sf.hibernate.cfg.Binder.bindProperty(Binder.java:454)
at net.sf.hibernate.cfg.Binder.createProperty(Binder.java:1081)
at net.sf.hibernate.cfg.Binder.propertiesFromXML(Binder.java:1061)
at net.sf.hibernate.cfg.Binder.bindRootClass(Binder.java:362)
at net.sf.hibernate.cfg.Binder.bindRoot(Binder.java:1256)
at net.sf.hibernate.cfg.Configuration.add(Configuration.java:253)
at net.sf.hibernate.cfg.Configuration.addInputStream(Configuration.java:289)
... 2 more
Exception in thread "main"
Name and version of the database you are using: PostgreSQL 8.0.1
The generated SQL (show_sql=true): No sql was generated when the error was thrown.
Debug level Hibernate log excerpt: