-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 12 posts ] 
Author Message
 Post subject: Descriminator as property
PostPosted: Wed May 04, 2005 6:47 am 
Newbie

Joined: Wed May 04, 2005 5:45 am
Posts: 6
I have a class hierarchy of resources where I want to have a type parameter as descriminator. But I also want the type parameter to be accessible as a normal hibernate property.

I have searched the forum and found that this should be possible to achieve by defining the property with update=false and insert=false. But the problem is that I can't get it to work.

I use xdoclet 1.2.2 and hibernate 2 (part of JBoss 3.2.7). My mapping file looks like this. Any help would be appreciated.


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping
>
<class
name="se.jadestone.aop.resource.model.Resource"
table="Resource"
dynamic-update="true"
dynamic-insert="false"
select-before-update="false"
optimistic-lock="version"
>

<id
name="id"
column="id"
type="java.lang.Integer"
>
<generator class="assigned">
<!--
To add non XDoclet generator parameters, create a file named
hibernate-generator-params-Resource.xml
containing the additional parameters and place it in your merge dir.
-->
</generator>
</id>

<discriminator
column="resourceType"
type="java.lang.String"
length="4"
/>

<version
name="version"
type="int"
column="version"
access="property"
unsaved-value="undefined"
/>

<property
name="type"
type="java.lang.String"
update="false"
insert="false"
access="property"
column="resourceType"
length="4"
/>

<property
name="priority"
type="int"
update="true"
insert="true"
access="property"
column="priority"
/>

<property
name="description"
type="java.lang.String"
update="true"
insert="true"
access="property"
column="description"
/>

<property
name="resourceData"
type="se.jadestone.aop.resource.util.BinaryBlobType"
update="true"
insert="true"
access="property"
column="resourceData"
/>

<!--
To add non XDoclet property mappings, create a file named
hibernate-properties-Resource.xml
containing the additional properties and place it in your merge dir.
-->
<subclass
name="se.jadestone.aop.resource.model.ImageResource"
dynamic-update="false"
dynamic-insert="false"
discriminator-value="i"
>
<property
name="imageInfo"
type="se.jadestone.aop.resource.util.BinaryBlobType"
update="true"
insert="true"
access="property"
column="imageInfo"
/>

<!--
To add non XDoclet property mappings, create a file named
hibernate-properties-ImageResource.xml
containing the additional properties and place it in your merge dir.
-->

</subclass>

</class>

</hibernate-mapping>


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 10, 2005 2:51 am 
Beginner
Beginner

Joined: Fri Feb 11, 2005 12:03 pm
Posts: 48
Location: Kiel, Germany
Hi,

I tried the same and it works for me.

My mappings is similar to yours.

Can you describe the problem with more details? Do you have some logging output?


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 10, 2005 3:28 am 
Newbie

Joined: Wed May 04, 2005 5:45 am
Posts: 6
Hi,

I get the correct value for the resourceType in the database but not in the java object.
If I create a new ImageResource:

..
ImageResource resource = new ImageResource(3);
..call some set methods..
..
HibernateSession.currentSession().save(resource);

the new row in the database gets resourceType = i but if call resource.getType() I get null. The null value probably comes from the property definition in my Resource class.
My resource class looks like this:

/**
* Represents a content resource
*
* @hibernate.class
* table="Resource"
* discriminator-value="-"
* @hibernate.discriminator
* column="resourceType"
* type="java.lang.String"
* length="4"
*/
public abstract class Resource extends Model{
private static final Logger log = Logger.getLogger(Resource.class);

private byte priority;
private String description;
private String type;
private byte[] resourceData;

/**
* Empty constructor for persistence framework
*/
public Resource() {}

/**
* Creates a resource with a certain id
*/
public Resource(int id) {
setId(new Integer(id));
}

/**
* Returns the resource type.
* @return The resource type
*
* @hibernate.property
* update="false"
* insert="false"
* length="4"
* column="resourceType"
*/
public String getType(){
return type;
}

/**
* Returns the priority of this resource, allowed values are 0-7
* @return The priority level for this resource (0-7)
*
* @hibernate.property
*/
public byte getPriority() {
return priority;
}

/**
* Sets the new priority level (only values 0-7 are allowed)
* @param priority The new priority level
*/
public void setPriority(byte priority) {
if(priority < 0 || priority > 7){
throw new IllegalArgumentException("Priority has to be between 0 and 7");
}
this.priority = priority;
}

/**
* Returns the resource description
* @return The resource desctiption
*
* @hibernate.property
*/
public String getDescription() {
return description;
}

/**
* Sets the new resource description
* @param description The new resource description
*/
public void setDescription(String description) {
this.description = description;
}

/**
* Returns the resource data
* @return The resource data
*
* @hibernate.property
* column="resourceData"
* type="se.jadestone.aop.resource.util.BinaryBlobType"
*/
public byte[] getResourceData() {
return resourceData;
}

/**
* Returns the resource data
* @param resourceData The resource data
*/
public void setResourceData(byte[] resourceData) {
this.resourceData = resourceData;
}
}


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 10, 2005 4:18 am 
Beginner
Beginner

Joined: Fri Feb 11, 2005 12:03 pm
Posts: 48
Location: Kiel, Germany
I do not have a real answer, but:

I tested again with my abstract superclass having no setter method for the type property. My test failed during creation of the SessionFactory (so it is not the same as your problem, but anyway).
I was able to solve the problem by definining
Code:
access="field"

for my type property. As far as I understood hibernate then uses reflection to determine and set the value of this property. Otherwise it needs the setter and getter methods.

Perhaps your problem will be solved if you define a setter for your type property, too?


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 10, 2005 6:52 am 
Newbie

Joined: Wed May 04, 2005 5:45 am
Posts: 6
Hi again,

I tested to create a set method and gave the property access=field but it didn't help.
When you do this, does your getType method return the correct value?


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 10, 2005 7:07 am 
Beginner
Beginner

Joined: Fri Feb 11, 2005 12:03 pm
Posts: 48
Location: Kiel, Germany
Yes, my getType method returns the right values.

Then I am out of ideas, sorry.


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 10, 2005 7:11 am 
Newbie

Joined: Wed May 04, 2005 5:45 am
Posts: 6
Can you post your java file and your hibernate mapping file that you got it to work with? I would be really kind of you.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 11, 2005 8:58 am 
Beginner
Beginner

Joined: Fri Feb 11, 2005 12:03 pm
Posts: 48
Location: Kiel, Germany
Here they are:

the superclass Account

Code:
package de.splendid.cca.modules.email.model.beans;

import java.util.Date;

/**
* Represents an account belonging to a
* {@link de.splendid.cca.modules.email.model.beans.Domain} of a
* {@link de.splendid.cca.model.beans.Company}.
*
* @hibernate.class
*   table="account"
* @hibernate.discriminator
*   column="account_type"
*
*/
public abstract class Account
   implements IAccount
{
   /**
    * the id of this account.
    */
   protected int accountId;
   protected String localpart;
   protected String description;
   protected Domain domain;
   protected Date creationDate;
   protected String accountType;
   
   protected Account()
   {
      /* hibernate needs default constructor */
   }
   
   public Account(String localpart, Domain domain)
   {
      this.localpart = localpart;
      this.domain = domain;
      domain.addAccount(this);
   }
   
   /**
    * @hibernate.property
    *   column="creation_date"
    *   not-null="true"
    */
   public Date getCreationDate()
   {
      return creationDate;
   }
   
   /**
    * @hibernate.property
    *   column="description"
    *   not-null="true"
    */
   public String getDescription()
   {
      return description;      
   }
   
   /**
    * @hibernate.many-to-one
    *   column="domain_id"
    *   not-null="true"
    *   cascade="save-update"
    *
    * @return
    */
   public Domain getDomain()
   {
      return domain;
   }
   
   /**
    * @hibernate.property
    *   column="localpart"
    *   not-null="true"
    */
   public String getLocalpart()
   {
      return localpart;
   }
   
   /**
    * @hibernate.id
    *   column="account_id"
    *   generator-class="identity"
    *
    * @return
    */
   public int getAccountId()
   {
      return accountId;
   }
   
   public void setAccountId(int accountId)
   {
      this.accountId = accountId;
   }
   
   public void setCreationDate(Date creationDate)
   {
      this.creationDate = creationDate;
   }
   
   public void setDescription(String description)
   {
      this.description = description;
   }
   
   public void setLocalpart(String localpart)
   {
      this.localpart = localpart;
   }
   
   /**
    * Returns the name of the underlying domain.
    *
    * @return the name of the underlying domain
    */
   public String getDomainName()
   {
      return domain.getName();
   }
   
   /**
    * Returns the email address of this account which is
    * <code>localpart</code>&#x40;<code>domainName</code>.
    *
    * @return the email address of this account
    */
   public String getEmailAddress()
   {
      return getLocalpart()+"@"+getDomainName();
   }
   
   /**
    * Two accounts are equal if they have the same localpart and
    * domain.
    */
   public boolean equals(Object obj)
   {
      boolean result = true;
      try {
         Account otherAccount = (Account) obj;
         result = (this.localpart.equals(otherAccount.localpart)) &&
            (this.domain.equals(otherAccount.domain));
      } catch (ClassCastException e) {
         result = false;
      }
      
      return result;
   }
   
   public int hashCode()
   {
      return this.localpart.hashCode()+this.domain.getName().hashCode();
   }
   
   protected void setDomain(Domain domain)
   {
      this.domain = domain;
   }
   
   /**
    * The hibernate mapping definition comes from a merge file
    * since xdoclet 1.2.2 does not seem to support the 'insert' and
    * 'update' attributes of the (@)hibernate.property. We
    * need these attributes since this property is the discriminator
    * for the subclasses.
    *
    * @return
    */
   public String getAccountType()
   {
      return accountType;
   }
   
   public void setAccountType(String accountType)
   {
      this.accountType = accountType;
   }
}


The xdoclet-merge file is

Code:
<!--
  hibernate-properties-Account.xml
  xdoclet 1.2.2 does not support the insert and update attribute
  in a (@)hibernate.property. Therefore we need to merge this
  property.
-->
<property
  name="accountType"
  type="java.lang.String"
  column="account_type"
  not-null="true"
  insert="false"
  update="false"
/>


One of the subclasses

Code:
package de.splendid.cca.modules.email.model.beans;

import java.util.Date;
import java.util.Set;


/**
* A real account has a physical size on a mail cluster.
*
* @hibernate.subclass
*   discriminator-value="RA"
*
*/
public class RealAccount
   extends Account
{
   /**
    * The size of the account in MB.
    */
   private int size;
   
   /**
    * The amount of MB already in use.
    * This value can only be achieved by requesting it from the
    * mail cluster. When this value is set the timestamp
    * {@link #used_updated} will be updated.
    */
   private int used;
   
   /**
    * Timestamp in milliseconds since 1.1.1970
    * of the last update of the {@link #used} value.
    * May be 0 to indicate that the {@link #used} value is
    * still undefined.
    * This value can not be set directly, only indirectly via
    * {@link #setUsed(int)}.
    */
   private long usedUpdated;
   
   /**
    * The owner of the account in plain text.
    */
   private String owner;

   /**
    * The set of {@link AliasAccount alias accounts}.
    */
   private Set aliasSet;
   
   protected RealAccount()
   {
      /* Hibernate needs default constructor */
   }
   
   public RealAccount(
         String localpart,
         Domain domain,
         int size,
         String owner)
   {
      super(localpart,domain);      
      this.size = size;
      this.owner = owner;
      this.creationDate = new Date();
      this.description = "";
   }
   
   /**
    * @hibernate.property
    *   column="owner"
    *
    * @return
    */
   public String getOwner()
   {
      return owner;
   }
   
   public void setOwner(String owner)
   {
      this.owner = owner;
   }
   
   /**
    * @hibernate.property
    *   column="size"
    *
    * @return
    */
   public int getSize()
   {
      return size;
   }
   
   public void setSize(int size)
   {
      this.size = size;
   }
   
   /**
    * @hibernate.property
    *   column="used"
    *
    * @return
    */
   public int getUsed()
   {
      return used;
   }

   /**
    * Sets the value {@link #usedUpdated} to the current time
    * ({@link System#currentTimeMillis()}), too.
    *
    * @param used
    */
   public void setUsed(int used)
   {
      this.used = used;
      this.usedUpdated = System.currentTimeMillis();
   }
   
   /**
    * @hibernate.property
    *   column="used_updated"
    *
    * @return
    */
   public long getUsedUpdated()
   {
      return usedUpdated;
   }
   
   /**
    * // TODO define hibernate mapping   
    * @return
    */
   public Set getAliasSet()
   {
      return aliasSet;
   }
   
   public void setAliasSet(Set aliasSet)
   {
      this.aliasSet = aliasSet;
   }
   
   protected void setUsedUpdated(long usedUpdated)
   {
      this.usedUpdated = usedUpdated;
   }
   
   public String toString()
   {
      StringBuffer result = new StringBuffer();
      
      result.append("RealAccount[");
      result.append(getEmailAddress());
      result.append(",size=").append(getSize()).append("MB");
      result.append(",owner=").append(getOwner());
      result.append(",type=").append(getAccountType());
      result.append("]");
      return result.toString();
   }
   
   public RealAccount addAliasAccount(AliasAccount aliasAccount)
   {
      this.getAliasSet().add(aliasAccount);
      aliasAccount.setRealAccount(this);
      
      return this;
   }
}


hibernate mapping file

Code:
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping
>
    <class
        name="de.splendid.cca.modules.email.model.beans.Account"
        table="account"
    >

        <id
            name="accountId"
            column="account_id"
            type="int"
        >
            <generator class="identity">
              <!-- 
                  To add non XDoclet generator parameters, create a file named
                  hibernate-generator-params-Account.xml
                  containing the additional parameters and place it in your merge dir.
              -->
            </generator>
        </id>

        <discriminator
            column="account_type"
        />

        <property
            name="creationDate"
            type="java.util.Date"
            column="creation_date"
            not-null="true"
        />

        <property
            name="description"
            type="java.lang.String"
            column="description"
            not-null="true"
        />

        <many-to-one
            name="domain"
            class="de.splendid.cca.modules.email.model.beans.Domain"
            cascade="save-update"
            outer-join="auto"
            column="domain_id"
            not-null="true"
        />

        <property
            name="localpart"
            type="java.lang.String"
            column="localpart"
            not-null="true"
        />

    <!--
  hibernate-properties-Account.xml
  xdoclet 1.2.2 does not support the insert and update attribute
  in a (@)hibernate.property. Therefore we need to merge this
  property.
-->
<property
  name="accountType"
  type="java.lang.String"
  column="account_type"
  not-null="true"
  insert="false"
  update="false"
/>
        <subclass
            name="de.splendid.cca.modules.email.model.beans.AliasAccount"
            discriminator-value="AA"
        >

        <many-to-one
            name="realAccount"
            class="de.splendid.cca.modules.email.model.beans.RealAccount"
            cascade="none"
            outer-join="auto"
            column="real_account_id"
        />

       <!--
               To add non XDoclet property mappings, create a file named
                hibernate-properties-AliasAccount.xml
      containing the additional properties and place it in your merge dir.
       -->

        </subclass>
        <subclass
            name="de.splendid.cca.modules.email.model.beans.RealAccount"
            discriminator-value="RA"
        >
        <property
            name="owner"
            type="java.lang.String"
            column="owner"
        />

        <property
            name="size"
            type="int"
            column="size"
        />

        <property
            name="used"
            type="int"
            column="used"
        />

        <property
            name="usedUpdated"
            type="long"
            column="used_updated"
        />

       <!--
               To add non XDoclet property mappings, create a file named
                hibernate-properties-RealAccount.xml
      containing the additional properties and place it in your merge dir.
       -->

        </subclass>
        <subclass
            name="de.splendid.cca.modules.email.model.beans.MailList"
            discriminator-value="ML"
        >

        <set
            name="accountSet"
            table="maillist_account"
            lazy="false"
            cascade="none"
            sort="unsorted"
        >

              <key
                  column="maillist_id"
              >
              </key>

              <many-to-many
                  class="de.splendid.cca.modules.email.model.beans.Account"
                  column="account_id"
                  outer-join="auto"
               />

        </set>

        <set
            name="externalAccountSet"
            table="maillist_externalaccount"
            lazy="true"
            cascade="none"
            sort="unsorted"
        >

              <key
                  column="maillist_id"
              >
              </key>

              <many-to-many
                  class="de.splendid.cca.modules.email.model.beans.ExternalAccount"
                  column="external_account_id"
                  outer-join="auto"
               />

        </set>

        <property
            name="name"
            type="java.lang.String"
            column="maillistname"
        />

       <!--
               To add non XDoclet property mappings, create a file named
                hibernate-properties-MailList.xml
      containing the additional properties and place it in your merge dir.
       -->

        </subclass>

    </class>

</hibernate-mapping>



An example of a query build by hibernate is
Code:
select accountset0_.domain_id as domain_id__, accountset0_.account_id as account_id__, accountset0_.account_id as account_id0_, accountset0_.account_type as account_2_0_, accountset0_.creation_date as creation3_0_, accountset0_.description as descript4_0_, accountset0_.domain_id as domain_id0_, accountset0_.localpart as localpart0_, accountset0_.real_account_id as real_acc7_0_, accountset0_.owner as owner0_, accountset0_.size as size0_, accountset0_.used as used0_, accountset0_.used_updated as used_up11_0_, accountset0_.maillistname as maillis12_0_ from account accountset0_ where accountset0_.domain_id=? order by accountset0_.localpart


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 11, 2005 11:09 am 
Newbie

Joined: Wed May 04, 2005 5:45 am
Posts: 6
Thank you for your posted example.

This just gets even more strange. I changed my hibernate mapping file so it looks like yours but it still doesn't work. I get a sql query that is equivalent to yours. If I do a select for a resource subtype it automaticly adds a where condition (type=i), so the discriminator works. And the values are still correct in the database but if I call getType() on a resource object I get null.

So my question is if you get a RealAccount object from the database and call getAccountType() on that object do you get the string RA then?


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 11, 2005 11:33 am 
Beginner
Beginner

Joined: Fri Feb 11, 2005 12:03 pm
Posts: 48
Location: Kiel, Germany
Yes, I do.

And your class Resource has a setter method
void setType(String) ?


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 11, 2005 12:42 pm 
Newbie

Joined: Wed May 04, 2005 5:45 am
Posts: 6
Yes, my Resource class contains the type attribute and has a set and a get method for that attribute.

I added some debug output and if I set insert=false and update=false my setType method never gets called. But if I set insert=true I get an exception, which isn't strange. On the other hand if setType never gets called getType will probably always return null because then the type attribute has never been set, or?

My resource class:

/**
* Represents a content resource
*
* @hibernate.class
* table="Resource"
* @hibernate.discriminator
* column="type"
*/
public abstract class Resource extends Model{
private static final Logger log = Logger.getLogger(Resource.class);

private String description;
private byte[] resourceData;
protected String type;

/**
* Empty constructor for persistence framework
*/
public Resource() {}

/**
* Creates a resource with a certain id
*/
public Resource(int id) {
setId(new Integer(id));
}

/**
*
* @return
*
* @hibernate.property
* column="type"
* not-null="true"
* insert="false"
* update="false"
*/
public String getType(){
return type;
}

public void setType(String type){
this.type = type;
}

/**
* Returns the resource description
* @return The resource desctiption
*
* @hibernate.property
*/
public String getDescription() {
return description;
}

/**
* Sets the new resource description
* @param description The new resource description
*/
public void setDescription(String description) {
this.description = description;
}

/**
* Returns the resource data
* @return The resource data
*
* @hibernate.property
* column="resourceData"
* type="se.jadestone.aop.resource.util.BinaryBlobType"
*/
public byte[] getResourceData() {
return resourceData;
}

/**
* Returns the resource data
* @param resourceData The resource data
*/
public void setResourceData(byte[] resourceData) {
this.resourceData = resourceData;
}
}

My subclass, ImageResource:

/**
* Represents a content resource as an image
*
* @hibernate.subclass discriminator-value="Image"
*/
public class ImageResource extends Resource {
private static final Logger log = Logger.getLogger(ImageResource.class);

private byte[] imageInfo;

/**
* Empty constructor for persistence framework
*/
public ImageResource() {}

/**
* Creates a resource with a certain id
*/
public ImageResource(int id) {
super(id);
}

/**
* Returns information about the content in the image data
* @return Information about the content in the image data
*
* @hibernate.property
* column="imageInfo"
* type="se.jadestone.aop.resource.util.BinaryBlobType"
*/
public byte[] getImageInfo() {
return imageInfo;
}

/**
* Returns the inormation about the image content (x, y, width and height values)
* @param imageInfo Information about the image content
*/
public void setImageInfo(byte[] imageInfo) {
this.imageInfo = imageInfo;
}
}


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 12, 2005 3:08 am 
Beginner
Beginner

Joined: Fri Feb 11, 2005 12:03 pm
Posts: 48
Location: Kiel, Germany
I think you are right.

The only difference I see now is that your class Resource is a subclass of Model.
Is there something about this class?

I suggest starting with a smaller example: superclass and subclass with just one property (the discriminator). If that works enhance the example


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 12 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.