The problem looks like this:
A Product table. 3 joined-subclasses: DVD, CD, Book.
A Role table (composite id: NAME, ROLE, PRODUCT), and subclasses by discriminator column ROLE: actor, director, artist, author, etc.. which are mapped to Actor, Director, Artist, Author java classes; (pretty usual I guess) ...
A Book has authors, a CD artists, a DVD actors and directors - these are all modelled via set with a one-to-many relation pointing to the class of the items, e.g. Author, Director, Artist, etc.. which are just subclasses of Role (see 2.)
As long as a joined-subclass has only one such set (of people/roles), all works fine. But when it has two, like DVD, actors and directors, Hibernate throws a WrongClassException?
XML excerpt (Product.hbm.xml):
<joined-subclass name="media.DVD" table="V_DVD" lazy="false">
<key column="IDPRODUCT"/>
<property column="FORMAT" name="format" type="string"/>
...
<set name="actors" lazy="false">
<key column="IDPRODUCT"/>
<one-to-many class="media.DVD$Actor"/>
</set>
<set name="directors" lazy="false">
<key column="IDPRODUCT"/>
<one-to-many class="media.DVD$Director"/>
</set>
...
</joined-subclass>
XML (Person.hbm.xml):
<class name="media.Person" table="V_ROLE">
<composite-id>
<key-property column="NAME" name="name" type="string"/>
<key-property column="ROLE" name="role" type="string"/>
<key-many-to-one class="media.Product" column="IDPRODUCT"
name="product"/>
</composite-id>
<discriminator column="ROLE" insert="false" type="string"/>
<property name="name"/>
<property name="role"/>
<many-to-one class="media.Product" column="IDPRODUCT"
insert="false" name="product" update="false"/>
<subclass discriminator-value="author" name="media.Book$Author"/>
<subclass discriminator-value="artist" name="media.Music$Artist"/>
<subclass discriminator-value="creator" name="media.DVD$Creator"/>
<subclass discriminator-value="director" name="media.DVD$Director"/>
<subclass discriminator-value="actor" name="media.DVD$Actor"/>
</class>
It seems somewhat ok to me, yet it throws an exception.
Thank you for any idea!
I've got to say, this is a rather esoteric mapping. Why are you using a composite key (with discriminator being a part of it no less) instead of surrogate?
One possible issue here is you may be manually setting discriminator value (via role property) to something other than what it should be for given subclass. Hibernate won't be able to overwrite it (discriminator is mapped with insert="false") and this would cause a WrongClassException on subsequent select.
It's also possible that there' something funky going on in the Product mapping from which media.DVD extends and which you didn't include. Can you add that as well as provide full stack trace?
The problem is basically that your model is not normalized: You tell hibernate that there is a composite key, but you use only part of that key to reference the table.
I think you can implement this by either having a set of people in your product and each person has a role, or by mapping each set to a separate (linking) table with your person table.
Another more esoteric approach would be to map to a MultiMap: In fact you have a Map<Role, Set<Person>> in your product. Hibernate does not support this out of the box, but I once wrote a UserType for this.
Cheers,
-Maarten
Related
My question might look silly. I am new to JPA and trying to understand the underlying concepts of it. I found out that there is a #Many-to-one entity relationship that can be used there. My question is why someone would want to use this while have #One-to-many relationship?
I mean, having the latter one is enough to know the relationship and sending queries right?
if not please explain. Maybe the idea that I have about these two relationships are completely wrong. Please provide a scenario as an example so I understand better.
Thanks
OneToMany is used to map collections. If you want this relation to be bidirectional then you need to add a ManyToOne mapping on the other side.
With Hibernate mapping : Person N <---> 1 Address
<class name="Person">
<id name="id" column="personId"/>
<many-to-one name="address" column="addressId"/>
</class>
<class name="Address">
<id name="id" column="addressId"/>
<set name="people">
<key column="addressId"/>
<one-to-many class="Person"/>
</set>
</class>
ManyToOne can also be used to map a dependency between 2 objects. In this case if you want the relation to be bidirectional then you need to add a OneToOne mapping on the other side.
With Hibernate mapping : Person 1 <---> 1 Address
<class name="Person">
<id name="id" column="personId"/>
<many-to-one name="address" column="addressId" unique="true"/>
</class>
<class name="Address">
<id name="id" column="addressId"/>
<one-to-one name="person" property-ref="address"/>
</class>
#ManyToOne and #OneToManyare both part of the same concept used to describe the two sides of a single bidirectional relationship.
If you want the relationship to be unidirectional - i.e. one of the entities cannot traverse to the other, you can use one of the annotations. To decide which one, you will have to decide which entity should know about the relation and which should not.
But often you want a bidirectional relation - then you use both.
I have tables like Trade_PAC_UNADJUSTED and Trade_PAC_ADJUSTED. Similary for other regions .The table structure is same and they all belong to the same schema. How do i map these tables. I don't want to duplicate the properties of the class entries in .hbm file. Also my POJO class will be same for all of them.
Please help.
Is it possible to have something like this :
<class name="com.Bean" table="TRADE" entity-name="TRADE">
<discriminator type="string">
<column name="PROCESSING_LOCATION" />
</discriminator>
<property name="..........></property>
<subclass name ="com.Bean" discriminator-value="PAC">
<discriminator type="integer">
<column name="RUN_ID" />
</discriminator>
<subclass name ="com.Bean" discriminator-value="1" entity-name="TRADE_PAC_UADJUSTED"/>
<subclass name ="com.Bean" discriminator-value="2,3,4,5,6,7,8,9" entity-name="TRADE_PAC_ADJUSTED"/>
</subclass>
</class>
But there is no way i can pass the table name within subclass ?
If you create a new POJO object and ask Hibernate to save it, how will it know which of your tables it should insert into? You will need something in your POJO to tell it which way to go.
You might be able to do this using a Hibernate "discriminator" to treat this as an inheritance scenario.
I have designed a table (in postgres) where 'id' column is autoincremented via its SEQUENCE entity.
But when I began use hibernate I met the problem .... due creating the insert statement, it use the follow statement
INSERT INTO mytable (id, name) VALUE (0, 'blablabla')
...
but I want it make somethink like that:
INSERT INTO mytable (name) VALUE ('blablabla')
... Postgres have to generate id automatically (at least when I ran such scripts withing sql editor, it worked)
I belive it can be configured, but I don't know how...
Here is my part of my .hbm.xml:
<hibernate-mapping>
<class name="com.cse.database.bean.Category" table="category">
<id name="id" type="int">
<column name="id" />
<generator class="assigned" />
</id>
<property name="name" type="string">
<column name="name" length="100" not-null="true" unique="true" />
</property>
</class>
</hibernate-mapping>
When you specify a <generator>, you're telling Hibernate how to create the ID. assigned means your application is doing it (which you don't want). You can specify a Hibernate class or even an application class to do it, which you don't want either. identity means the database does it for you, which is what you want. In some databases you can use sequence (which will query the sequence generator to get the ID, then write the record), and Hibernate allows you to use native to specify the most applicable for your DB.
<id name="id" type="int">
<column name="id" />
<generator class="identity" />
</id>
apparently works in this case.
assigned means that you explicitely assign the ID of the entity, which is not the case. The problem with these auto-generated IDs in postgreSQL, AFAIK, is that it's not possible to get the last generated ID, and that Hibernate has no way to get the ID assigned to the entity after it has inserted it. You should not use an autogenerated ID in PostgreSQL.
Instead, let Hibernate use a sequence generator and assign the ID to the entity before inserting it:
#SequenceGenerator(name = "FOO_SEQ", sequenceName = "FOO_SEQ")
public class Foo {
#Id
#GeneratedValue(generator = "FOO_SEQ")
#Column(name = "FOO_ID")
private Long id;
I'm trying to map my Hashmap in Hibernate.
All examples I can find are simply like this:
class FooBar{
Map<String,String> myStrings;
}
Which would simply map to
<map role="ages">
<key column="id"/>
<index column="name" type="string"/>
<element column="age" type="string"/>
</map>
However, I use a more object-oriented approach in my Java code. Kind of like the following:
class Bar{
private Map<String, Foo> myFoos;
}
How would I go about mapping this? As the relationshop?
Of otherwise defined: How can I map a one-to-many in a Map?
Thanks,
Bart
There are a few examples in the Hibernate reference manual chapter on Collection Mapping. You would want to do something like
<map name="foos">
<key column="id"/>
<index column="name" type="string"/>
<one-to-many class="Foo"/>
</map>
The difference is <one-to-many class="Foo"/> - this will map the relationship by using a foreign key column to the ID of the Foo table in the parent table (i.e. the object that has the Map of foos).
There are several more flavors and variations of how you can map this based on exactly the type of relationship you want, see the manual for more examples.
Suppose there are three tables:
Table A (ID, NAME)
Table B (ID, NAME)
Table A-B (A_ID, B_ID, Boolean_Property)
and Table A and B are modeled by classes A and B as follows:
public class A {
private long id;
private String name;
// All the B's that belong to A where the boolean property is True
Set<B> trueBSet;
// All the B's that belong to A where the boolean property is False
Set<B> falseBSet;
}
public class B {
private long id;
private String name;
}
How would I model this using Hibernate? I would like to be able to do the following, but it appears that discriminator column values don't exist for set attributes:
<class name="A" table="A">
<id name="id" column="ID">
<generator class="native" />
</id>
<property name="name" column="NAME" />
<set name="trueB" table="A-B">
<key column="A_ID"/>
<many-to-many column="B_ID" class="B"/>
<discriminator column="Boolean_Property" value="True" />
</set>
<set name="falseB" table="A-B">
<key column="A_ID"/>
<many-to-many column="B_ID" class="B"/>
<discriminator column="Boolean_Property" value="False" />
</set>
</class>
I think, you can apply where clause in set tag. Hibernate document state as follows in following link
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/collections.html
where (optional): specifies an arbitrary SQL WHERE condition that is used when retrieving or removing the collection. This is useful if the collection needs to contain only a subset of the available data.
as #ChssPly76 said this is a duplicate of
Multiple #ManyToMany sets from one join table
The solutions there suggest to use a view as Join-Column and plain SQL for CRUD operations.
This Answer is marked community wiki so if anyone else wants this question to leave the 'unanswered' section may freely upvote it.