-->
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.  [ 20 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Having "discriminator-value" Set Via SQL
PostPosted: Tue Dec 06, 2005 11:39 am 
Beginner
Beginner

Joined: Thu Dec 01, 2005 1:09 pm
Posts: 33
Hi,

I have several independent classes which are mapped to one table.

One column (int32 CODE) contains a code by which the classes are to be distinguished. There also is one class which doesn't make use of the CODE column.

From what I've seen in the documentation, the <discriminator> tag is what I want.

The value of the CODE column however may differ among different databases.
Is there a way to write something like this?

Code:
<subclass name="CashPayment" discriminator-value="(select PAYMENT_TYPE from PAYMENT_CODE where NAME='CASH')">
...
</subclass>


So that the discriminator-value would get initialised at program startup.

Thanks in advance.

Regards,

Marvin


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 06, 2005 4:14 pm 
Regular
Regular

Joined: Sat Nov 19, 2005 2:46 pm
Posts: 69
Hi GrønnDemon,

What you need is the formula="" syntax on the <discriminator> tag.

For example, I have several type_codes (about 6), which configure to 3 different sub-classes. 2 type_codes map directly, the other 4 get bunched into the 3rd sub-class:
Code:
<class name="" table="">
...
  <discriminator>
    <formula>CASE WHEN type_code NOT IN ('RET', 'COM') THEN 'BUS' ELSE type_code END</formula>
  </discriminator>
...
  <subclass name="" discriminator-value="RET"/>
  <subclass name="" discriminator-value="COM"/>
  <subclass name="" discriminator-value="BUS"/>
</class>


I think you can use any arbitrary SQL in the formula, as long as it amounts to something you can use in a SELECT statement (including sub selects).

If you have a sub-class that doesn't care, use discriminator-value="not null"

Hope that helps. (Don't forget to rate :-)

_________________
Stewart
London, UK


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 06, 2005 4:17 pm 
Beginner
Beginner

Joined: Thu Dec 01, 2005 1:09 pm
Posts: 33
Thanks a lot! :)


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 08, 2005 10:27 am 
Beginner
Beginner

Joined: Thu Dec 01, 2005 1:09 pm
Posts: 33
How would I have the values you called 'RET' and 'COM' initialised at application startup?

Those values may differ on different databases, but will be constant during program execution. I wouldn't want to write something like

Code:
<discriminator>
    <formula>CASE WHEN type_code NOT IN ((SELECT ...), (SELECT ...)) THEN 'BUS' ELSE type_code END</formula>
  </discriminator>


for performance reasons.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 08, 2005 10:41 am 
Regular
Regular

Joined: Sat Nov 19, 2005 2:46 pm
Posts: 69
The values I used are just arbitrary constants. I could have used A, B, C or anything.

Not sure I understand exactly what you mean.
If you are applying this to different databases, then won't you be looking at a different set of hibernatecfg.xml and *.hbm.xml files anyway?

If you're running of different databases, maybe you could use a compile-time token replacement system, such as Ant can be used for?

Otherwise, without knowing your full requirements, I can only suggest that I don't see a problem with your nested double select statements.

Stewart

_________________
Stewart
London, UK


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 08, 2005 10:49 am 
Beginner
Beginner

Joined: Thu Dec 01, 2005 1:09 pm
Posts: 33
In my case, it wouldn't be constants but PK's (or is it called an FK in this case?).

So the constant would be something like this:

Code:
select PK from SOMETABLE where NAME='FOOBAR';


FOOBAR is constant, but the discriminator value would be its PK.
Since the PK does not change, I wouldn't want the above statement to be executed every time I load an object as I guess it would be bad for my program's performance.

Instead, I want that statement to be executed once at program startup and the PK then to be used in the discriminator formula.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 08, 2005 11:06 am 
Regular
Regular

Joined: Sat Nov 19, 2005 2:46 pm
Posts: 69
I'm sorry, the whole point of a database is that the data is changeable at runtime.

If data does not change at runtime, then it is known as configuration. You could keep web.xml and struts-config.xml and log4j.properties in a database, but why? It only changes at compile/install time.

Maybe what you need is a stored procedure? Perhaps get_class_discriminator() ?

When I said that I had arbitrary constants, A, B, C, what I meant is, they are foreign keys to another table, but I am using them to discriminate between classes, because that's actually part of the significance assigned to that FK by the schema design.

In other words, I feel like I'm actually still guessing at what you mean or want.
If you feel able to fully describe that section of your schema and your class model, I may be able to come up with a suggestion.

Also, have you actually evaluated how many milli-seconds you're losing by doing an extra select?
Run your 2 selects (the "pre-loaded" one, and the one with nested selects in the case statement) on your SQL command line and find out how much time you're talking about here.
It might not be worth it.

S

_________________
Stewart
London, UK


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 12, 2005 3:16 pm 
Beginner
Beginner

Joined: Thu Dec 01, 2005 1:09 pm
Posts: 33
Ok, let me give you some details:

There is one table I'll call REFERENCES.

It contains the following columns:

Code:
REFERENCE_PK: the primary key
A_FK: a foreign key
B_FK: a foreign key
C_FK: a foreign key
REF_CODE: a string value describing the kind of reference
REF_DATA: reference data
DATA_LONG: more detailed data
DESCRIPTION: a description of this reference


This table is used to attach data to objects in the table.

All "objects" in the database have an "SK." For example, there is a table I'll call ANIMALS, they have an "ANIMAL_ID" (the primary key) and a column "ANIMAL_SK" (the only exception is the REFERENCES table, where the PK is used). This value is serialised in another table called OBJECTS:

Code:
OBJECT_ID: the primary key
REFTABLE: the table this object belongs to


This is how it works:

At least one column of A_FK, B_FK or C_FK should contain a value.

Let's assume A_FK

This value is then looked up in the OBJECTS table. If REFTABLE is "ANIMALS", this would mean the referenced object is that in the ANIMALS table with ANIMAL_SK=A_FK.

The same works with B_FK and C_FK. This way objects can be connected to each other.

REF_CODE would contain a code for this kind of connection, like "Image" if an image is being referenced.
Of course simple things like an animal's owner etc. are included in the respective table (like an ANIMAL's OWNER_ID etc.).

The advantage with this approach is that all kinds of references can be established without having to change the data model.

Now we'd like to add a certain code to an animal as a kind of upgrade to the the original product. As an example, I'll take an URL to a webpage with further information on an ANIMAL.

In this case, we'd add a row to REFERENCES with a REFERENCE_PK, no FK's, a certain REF_CODE that starts with an underscore ('_'), marking it as an internal value, and optionally some REF_DATA, DATA_LONG and DESCRIPTION values.
Rows with a REF_CODE beginning with an underscore will not be shown in the GUI.

We would then add a row to the OBJECTS table containing the REFERENCE_PK in the OBJECT_ID field and REFERENCES in the REF_TABLE column.

Now we can add rows to the REFERENCES table with a REF_CODE like "Website", an A_FK equal to the REFERENCE_PK of the row we just created and a B_FK to an ANIMAL. REF_DATA includes the URL.

The additional row with the underscore is desired because the customer cannot change the FK values, this way we keep him from fiddling with out program logic by creating a row with a REF_CODE="Website".

So REF_CODE has to be "Website" and one of the FK's has to match a value in OBJECTS that would point back to the row in REFERENCES.

Since the user may have added many other REFERENCES before, that FK cannot be known in advance, but it will be constant all the time. This is why I'd like it to be loaded at application startup.

I find this system very complicated myself and I'm not 100% sure if I've understood everything correctly since my co-worker only gave me a short introduction.

If you have any questions, do ask. Thank you very much for your support. :)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 13, 2005 7:21 am 
Regular
Regular

Joined: Sat Nov 19, 2005 2:46 pm
Posts: 69
I tried to draw diagram of how that design works. I think I got lost.

Let's see if I can summarise, and you can tell me if I got it correct.
You have a table called ANIMALS and this maps to an Animal class.
Now you want to add a property to this class of a url.
So for example:
Code:
Animal tiger = new Animal();
tiger.setName("Tiger");
tiger.setSpecies("Panthera tigris");
tiger.setUrl("http://en.wikipedia.org/wiki/Tiger"); // new property


But you keep your properties dynamically in this REFERENCES table, so that you have a row for 'name', a row for 'species' and now a new row for 'url'.

And you use the objects table because the reference itself may be an object, eg, if you had a SPECIES table which mapped to a Species class:
Species species = new Species();
species.setGenus("Panthera");
species.setSpecies("Tigris");
and so you actually would do, for an animal:
tiger.setSpecies(species);

and in REFERENCES we now need a row for 'genus' and the row for 'species' can actually refer to this SPECIES table via the OBJECTS table.

Have I got that right?

If so, then I'm sorry, but I have only one thing to say: WTF!
It's not the worst database design I've heard of, but it's certainly "exotic"!
Try this one instead:
http://thedailywtf.com/forums/53093/ShowPost.aspx

My advice to you is to read this:
http://en.wikipedia.org/wiki/Simplicity

Apart from that, I'm sorry, you're on your own.....

Stewart

_________________
Stewart
London, UK


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 15, 2005 1:13 pm 
Beginner
Beginner

Joined: Thu Dec 01, 2005 1:09 pm
Posts: 33
Hi,

we decided to make everything more simple.

Luckily we can go with one discriminator column.
It is possible to define classes in one mapping file and subclasses in another, right (using extends=...)?

How would I define something like "if no other discriminator-value matches, use this subclass?"

In this case, I won't be able to use the <formula> attribute as not all possible discriminator values are known yet: Further subclasses may be defined in other files.

Once again, thanks for your support.

Marvin


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 15, 2005 1:25 pm 
Regular
Regular

Joined: Sat Nov 19, 2005 2:46 pm
Posts: 69
Hi Marvin,

You can set discriminator-value="not null", but this will catch all values.
I have not tried it, but maybe you could combine "not null" with a where="" ?

I'm using it like this:
Code:
  <class name="LabelValueThingy" table="label_value_thingies" discriminator-value="not null">
    <id column="id" name="id" type="int" unsaved-value="-1">
      <generator class="native"/>
    </id>
    <discriminator column="group_name" type="string"/>
    <property column="group_name" length="50" name="groupName" type="string" insert="false" update="false"/>
    <property column="sequence" name="sequence" type="int"/>
    <property column="label" length="100" name="label" type="string"/>
    <property column="value" length="100" name="value" type="string"/>

  <subclass name="CountryThingy" discriminator-value="COUNTRY"/>
  <subclass name="CurrencyThingy" discriminator-value="CURRENCY"/>
  </class>


Notice my LabelValueThingy has discriminator-value set to "not null"
so if I wanted to make sure my LabelValueThingy did not include countries or currencies, I'd try
Code:
<class name="LabelValueThingy" table="label_value_thingies" discriminator-value="not null" where="group_name NOT IN('COUNTRY','CURRENCY')">

But I haven't tried this - just a suggestion

_________________
Stewart
London, UK


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 15, 2005 2:25 pm 
Beginner
Beginner

Joined: Thu Dec 01, 2005 1:09 pm
Posts: 33
Hmmm... we have one project that defines some standard values (=subclasses).

And we've got at least one "extension" that would define several other subclasses with their respective discriminator values.
Of course those projects should only depend on the main project and not on each other.

Is there a way to influence the order the discriminator values are checked?

To make it clearer, what I want is something like this:

Main.hbm.xml:
Code:
<class name="LabelValueThingy" table="label_value_thingies" discriminator-value="anything else">
    <id column="id" name="id" type="int" unsaved-value="-1">
      <generator class="native"/>
    </id>

    <discriminator column="group_name" type="string"/>

    <subclass name="CountryThingy" discriminator-value="COUNTRY"/>
    <subclass name="CurrencyThingy" discriminator-value="CURRENCY"/>
</class>


ProjectA.hbm.xml:
Code:
<subclass name="AnimalThingy" extends="LabelValueThingy" discriminator-value="ANIMAL">
</subclass>


ProjectB.hbm.xml:
Code:
<subclass name="CarThingy" extends="LabelValueThingy" discriminator-value="CAR">
</subclass>


Now all rows with "group_name NOT IN ('COUNTRY', 'CURRENCY', 'ANIMAL', 'CAR')" should be mapped to LabelValueThingy.

It would also be ok for me to have an extra subclass for this.

In Main.hbm.xml we just don't know about other mapped classes.

Marvin


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 15, 2005 2:29 pm 
Beginner
Beginner

Joined: Thu Dec 01, 2005 1:09 pm
Posts: 33
Maybe discriminator-value="not null" (for the superclass) and "force=true" (for the discriminator) would be the solution?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 15, 2005 2:38 pm 
Regular
Regular

Joined: Sat Nov 19, 2005 2:46 pm
Posts: 69
From Hibernate Reference PDF page 56 (downloadable from this site) :
Quote:
The force attribute is (only) useful if the table contains rows with "extra" discriminator values that are not
mapped to a persistent class. This will not usually be the case.


Is that what you want?

Did using where="" work? Did you try it?

Also from hibernate reference PDF:
Quote:
where (optional) specify an arbitrary SQL WHERE condition to be used when retrieving objects of this class

_________________
Stewart
London, UK


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 15, 2005 4:17 pm 
Beginner
Beginner

Joined: Thu Dec 01, 2005 1:09 pm
Posts: 33
We have tried the following mapping code:

Code:
<class name="LabelValueThingy" table="label_value_thingies" discriminator-value="not null">
    <id column="id" name="id" type="int" unsaved-value="-1">
        <generator class="assigned"/>
    </id>
    <discriminator column="group_name" not-null="false" force="true" type="String">
    </discriminator>

    <subclass name="AnimalThingy, SomeOtherAssembly" discriminator-value="ANIMAL">
        <property name="Description" column="DESCRIPTION" type="String"></property>   
    </subclass>
</class>


Using "not null" throws an IndexOutOfRangeException, using "NOT NULL" creates an SQL statement with "... WHERE group_name in ('NOT NULL', 'ANIMAL')".

Maybe I should add that I use NHibernate, but I don't think this is the reason...

Marvin


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 20 posts ]  Go to page 1, 2  Next

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.