Many-to-Many relationship in DataNucleus (JDO) doesn't persist - java

I don't manage to persist a many-to-many link with DataNucleus using JDO. I have two classes Book and Shop. This is the orm mapping file:
<?xml version="1.0"?>
<!DOCTYPE orm PUBLIC
"-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
"http://java.sun.com/dtd/orm_2_0.dtd">
<orm>
<package name="com.mypackage.pojo">
<class name="Book" identity-type="datastore">
<datastore-identity>
<column name="BOOK_ID" />
</datastore-identity>
<field name="name">
<column length="100" jdbc-type="VARCHAR" />
</field>
<field name="shops" persistence-modifier="persistent"
table="BOOKS_SHOPS">
<collection element-type="com.mypackage.pojo.Shop" />
<join>
<column name="BOOK_ID" />
</join>
<element>
<column name="SHOP_ID" />
</element>
</field>
</class>
<class name="Shop" identity-type="datastore">
<datastore-identity>
<column name="SHOP_ID" />
</datastore-identity>
<field name="name">
<column length="50" jdbc-type="VARCHAR" />
</field>
<field name="books" persistence-modifier="persistent"
table="BOOKS_SHOPS">
<collection element-type="com.mypackage.pojo.Book" />
<join>
<column name="SHOP_ID" />
</join>
<element>
<column name="BOOK_ID" />
</element>
</field>
</class>
</package>
</orm>
I try to link a book to a shop and the other way around, like this:
shop.addBook(book);
book.addShop(shop);
Making these two objects persistent again doesn't do anything. Both before and after the little code snippet above, their ObjectState is detached-clean.
What could I be doing wrong?

I have a working solution, but have to admit I do not fully understand everything. Things work when the last field element is not defined as
<field name="books" persistence-modifier="persistent" table="BOOKS_SHOPS">
but as
<field name="books" persistence-modifier="persistent" mapped-by="shops">
This solved my problem.
See also the DataNucleus manual on JDO M-N Relationships. However, leaving the join and the element elements out, as is done in this example, didn't work for me. Another relevant link on the DataNucleus site is JDO Guides : M-N Relation. The code for this last example can be found on SourceForge. Unfortunately, this example also didn't work for me.
This is not really a good answer, but all that I have to offer for now...

Related

What is the syntax of package.jdo file for a self referential table

I have a self referential table that I am mapping with kodo jdo 4 (supplied from weblogic server 10.3.4.) The code I have will get through the enhancer, but when I try to use it I get the error:
<openjpa-1.1.1-SNAPSHOT-r422266:965591 fatal user error> kodo.jdo.FatalUserException: Missing table name for field "com.[...].jdo.Branch.branches". This field cannot reside in the owning class table.
at org.apache.openjpa.jdbc.meta.FieldMapping.mapJoin(FieldMapping.java:529)
the table is:
CREATE TABLE branch
(
id VARCHAR2(10) NOT NULL,
parentId VARCHAR2(10) NOT NULL,
[other fields deleted ...]
CONSTRAINT branch_pk PRIMARY KEY(id),
CONSTRAINT branch_fk_parent FOREIGN KEY(parentId) REFERENCES branch(id)
);
the class is :
public class Branch implements MenuPart, Serializable
{
private Branch parent;
private Set<Branch> branches = new HashSet<Branch> ();
private String id;
private Set<Leaf> leafs = new HashSet<Leaf> ();
private long ordering;
private String title;
//methods removed.
}
the package.jdo file is:
<class name="Branch" objectid-class="BranchId" table="SCHEMA.BRANCH">
<version strategy="none"/>
<field name="parent" table="SCHEMA.BRANCH">
<column name="SCHEMA.BRANCH.PARENTID" target="ID" />
</field>
<field name="branches" table="SCHEMA.BRANCH" >
<collection element-type="Branch"/>
<join>
<column name="SCHEMA.BRANCH.PARENTID" target="ID" />
</join>
</field>
<field name="id" column="ID" primary-key="true"/>
<field name="leafs" table="SCHEMA.LEAF">
<collection element-type="Leaf"/>
<join>
<column name="ID" target="BRANCHID" />
</join>
</field>
<field name="ordering" column="ORDERING"/>
<field name="title" column="TITLE"/>
</class>
I have gotten the functionality to work in kodo jdo 3.4 but now the syntax is different and the friendly manual isn't that helpful that I can find.
Perhaps its objecting to you defining the join-table for that relation to be the table for the class itself (which is obviously wrong).
Obviously Kodo is not fully JDO-compliant, and is dead as a project, so if you hit issues then you've got problems ...
Can you try
<class name="Branch" objectid-class="BranchId" table="SCHEMA.BRANCH">
<version strategy="none"/>
<field name="parent" table="SCHEMA.BRANCH">
<column name="SCHEMA.BRANCH.PARENTID" target="ID" />
</field>
<field name="branches" table="SCHEMA.BRANCH" >
<collection element-type="Branch"/>
<extension vendor-name="kodo" key="inverse-owner" value="parent"/>
</field>
<field name="id" column="ID" primary-key="true"/>
</class>
here is what finally worked
<class name="Branch" objectid-class="BranchId" table="EBIGP.BRANCH">
<version strategy="none"/>
<field name="parent">
<column name="EBIGP.BRANCH.PARENTID" target="ID" />
</field>
<field name="branches" mapped-by="parent" />
<field name="id" column="ID" primary-key="true"/>
<field name="leafs" default-fetch-group="true">
<collection element-type="Leaf"/>
<element column="BRANCHID"/>
</field>
<field name="ordering" column="ORDERING"/>
<field name="title" column="TITLE"/>
</class>
Apparently all it needed was a mapped-by="parent" attribute.

Hibernate: How to model a double parent/child relationship

I got a pretty simple parent/child relationship here, which looks like this:
Email servers have n folders.
Folders can have n (sub-)folders.
Folders have a reference to their parent folder as well as to the email server they belong to.
My mapping files look like this:
MailServer.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 02.05.2011 12:32:52 by Hibernate Tools 3.3.0.GA -->
<hibernate-mapping>
<class name="test.MailServer" table="MAILSERVER">
<id name="id" type="long" access="field">
<column name="MAIL_SERVER_ID" />
<generator class="native" />
</id>
<bag name="folders" table="FOLDERS" lazy="false" inverse="true" cascade="all-delete-orphan">
<key column="MAIL_SERVER_ID"></key>
<one-to-many class="test.Folder" />
</bag>
</class>
</hibernate-mapping>
Folder.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 04.05.2011 15:02:31 by Hibernate Tools 3.3.0.GA -->
<hibernate-mapping>
<class name="test.Folder" table="FOLDERS">
<id name="id" type="long" access="field">
<column name="FOLDER_ID" />
<generator class="native" />
</id>
<many-to-one name="mailServer" column="MAIL_SERVER_ID" not-null="true" />
<bag name="folders" table="FOLDERS" lazy="false" inverse="true" cascade="all">
<key column="PARENT_FOLDER_ID" not-null="false"></key>
<one-to-many class="test.Folder" />
</bag>
<many-to-one name="parentFolder" column="PARENT_FOLDER_ID" />
</class>
</hibernate-mapping>
The problem I got, is the following.
Let's say, I have the following hierachy:
- MyMailServer
Folder1
- Folder2
Subfolder
Folder3
When I call hibernateSession.save(mailServerInstance); or hibernateSession.update(mailServerInstance);, Hibernate correctly stores everything to the database. The parent column id's are filled correctly. Same for all other references.
BUT when I load the data, Hibernate reloads the folder hierachy like this:
- MyMailServer
Folder1
Folder2
Subfolder
Folder3
I understand the reason: Subfolder has a reference to its MailServer and thus, Hibernate ads it there instead of the folder where it belongs to.
But, how do I solve this problem?
Thanks in advance!
As you already understood what Hibernate does here is logical since you're fetching all folders for given mailServer. I don't think there is a way to achieve what you want in 1 single query (not a hibernate restriction, also with plain old SQL this is not possible).
I have a very similar case where I do the following:
get root folders for mail server (-> where parent folder is null)
for each folder get the child folders
Another solution is using Transfer Objects and you map the entities to the transfer objects so that the required structure is obtained.
It all depends on your use case which (if any) solution is appropriate. E.g. if you can execute an AJAX call each time a folder is expanded (tree structure) then the first solution is ideal.
I worked around this by replacing
<bag name="folders" table="FOLDERS" lazy="false" inverse="true" cascade="all-delete-orphan">
<key column="MAIL_SERVER_ID"></key>
<one-to-many class="test.Folder" />
</bag>
in MailServer.hbm.xml with
<one-to-one name="rootFolder" class="test.Folder" cascade="all" />
That did the trick.

Trying to serialize an object compactly using Castor

I'm using Castor to write out a map of user ID's to time intervals. I'm using it to save and resume progress in a lengthy task, and I'm trying to make the XML as compact as possible. My map is from string userID's to a class that contains the interval timestamps, along with additional transient data that I don't need to serialize.
I'm able to use a nested class mapping:
...
<field name="userIntervals" collection="map">
<bind-xml name="u">
<class name="org.exolab.castor.mapping.MapItem">
<field name="key" type="string"><bind-xml name="n" node="attribute"/></field>
<field name="value" type="my.package.TimeInterval"/>
</class>
</bind-xml>
</field>
...
<class name="my.package.TimeInterval">
<map-to xml="ti"/>
<field name="intervalStart" type="long"><bind-xml name="s" node="attribute"/></field>
<field name="intervalEnd" type="long"><bind-xml name="e" node="attribute"/></field>
</class>
...
And get output that looks like:
<u n="36164639"><value s="1292750896000" e="1292750896000"/></u>
What I'd like is the name, start, and end of the user in a single node like this.
<u n="36164639" s="1292750896000" e="1292750896000"/>
But I can't seem to finagle it so the start and end attributes in the "value" go in the same node as the "key". Any ideas would be greatly appreciated.
Nash,
I think to arrange the castor mapping is bit tricky.
If you want to have structure like
<u n="36164639" s="1292750896000" e="1292750896000"/>
Then you need to create a new pojo file where it will be having
all the three fields Key,intervalStart,intervalEnd.
And let the File name as KeyTimeInterval
And map it like the below.
<field name="userIntervals" collection="map">
<class name="org.exolab.castor.mapping.MapItem">
<field name="u" type="my.package.KeyTimeInterval">
<bind-xml name="u" node="element"/>
</field>
</class>
</field>
<class name="my.package.KeyTimeInterval">
<field name="key" type="String">
<bind-xml name="n" node="attribute"/></field>
<field name="intervalStart" type="long">
<bind-xml name="s" node="attribute"/></field>
<field name="intervalEnd" type="long">
<bind-xml name="e" node="attribute"/></field>
</class>
I think you should be able to use location on s and e. Try this:-
...
<class name="my.package.TimeInterval">
<map-to xml="ti"/>
<field name="intervalStart" type="long">
<bind-xml name="s" location="u" node="attribute"/>
</field>
<field name="intervalEnd" type="long">
<bind-xml name="e" location="u" node="attribute"/>
</field>
</class>
Am answering my own question here, since there is a solution that does exactly what I want, and there's actually an error in the explanation at http://www.castor.org/xml-mapping.html#Sample-3:-Using-the-container-attribute - the container attribute is exactly what's needed here.
Changing one line in the mapping:
<field name="value" type="my.package.TimeInterval" container="true"/>
did exactly what I wanted, it didn't create a subelement for the value, just mapped the fields into the existing parent element. Since then, I've used this quite a few times to map multiple-value classes into their parent.
The error of course is the documentation states you do this by setting the container attribute to false. Of course, it should be true.

Hibernate Cross Reference many-to-many Parent/Child Mapping Of Same Class

I'm having a major problem getting a parent/child relationship working for a hierarchy of a single class. Basically I have to represent a server tree like thus:
Server A
Server B
Server C
Server D
Server E
Server C
Server D
Server F
Server G
Note that the children of Servers B & E are the same. My original mapping was something like this, which was fine until I needed to have the objects for server C & D being the same instance, so having a single column for PARENT_ID got filled by the last relationship and only one of servers B or E would show the children:
<hibernate-mapping ...>
<class name="Server" ...>
...
<set name="children" cascade="all-delete-orphan" lazy="false">
<key column="PARENT_ID" />
<one-to-many class="Server" />
</set>
</class>
</hibernate-mapping>
I know I need to do some sort of cross reference table to map the fact that a server can have multiple parents, but all the examples I've found on-line contain a separate parent and child class.
Can anyone tell me how to do a cross reference parent/child mapping for the same class...? I.e. something like:
<hibernate-mapping ...>
<class name="Server" ...>
...
<set name="children" cascade="all-delete-orphan" lazy="false">
<key>
<column name="PARENT_ID" />
<column name="CHILD_ID" />
</key>
<many-to-many class="Server">
<column name="???" />
<formula>???</formula>
</many-to-many>
</set>
</class>
</hibernate-mapping>
Thanks,
Bob.
In your many-to-many mapping, set the column name to be CHILD_ID.
<many-to-many class="Server">
<column name="CHILD_ID" />
</many-to-many>
This will cause the relationship to view the child id as the id representing itself. While the one-to-many relationship will use the parent_id as the id representing itself. Should work, I haven't ran it, but I have done a similar thing before.
Doing the following:
...
<set name="children" table="SERVER_XREF" cascade="all-delete-orphan" lazy="false">
<key column="PARENT_ID" />
<many-to-many class="Server" column="CHILD_ID" />
</set>
...
appears to have resulted in the server hierarchy I was after being returned.
Cheers,

Legacy mapping with hibernate

For my current project I have to map a legacy database using hibernate, but I'm running into some problems.
The database is setup using one 'entity' table, which contains common properties for all domain objects. Properties include (among others) creation date, owner (user), and a primary key which is subsequently used in the tables for the domain objects.
A simple representation of the context is as such:
table entity
- int id
- varchar owner
table account
- int accountid (references entity.id)
table contact
- int contactid (references entity.id)
- int accountid (references account.accountid)
My problem exhibits itself when I try to add a collection mapping to my account mapping, containing all contacts belonging to the account. My attempts boil down to the following:
<hibernate-mapping>
<class name="Contact" table="entity">
<id name="id" column="id">
<generator class="native" />
</id>
<join table="contact">
<key column="contactid"/>
<!-- more stuff -->
</join>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="Account" table="entity">
<id name="id" column="id">
<generator class="native" />
</id>
<bag name="contacts" table="contact">
<key column="accountid" />
<one-to-many class="Contact"/>
</bag>
<join table="account">
<key column="accountid"/>
<!-- more stuff -->
</join>
</class>
</hibernate-mapping>
However, when I try to fetch the account I get an SQL error, stating that the entity table does not contain a column called accountid. I see why this is happening: the mapping tries to find the accountid column in the entity table, when I want it to look in the contact table. Am I missing something obvious here, or should I approach this problem from another direction?
This looks to me like you actually need to be mapping an inheritance, using the Table Per Subclass paradigm.
Something like this:
<class name="entity" table="entity">
<id name="id" column="id">
...
</id>
<joined-subclass name="contact" table="contact">
<key column="contactid"/>
</joined-subclass>
<joined-subclass name="account" table="account">
<key column="accountid"/>
</joined-subclass>
</class>
That's approximate by the way - it's described in detail in section 9.1.2 of the Hibernate documentation (just in case you can't find it, it's called "Table per subclass").
Cheers
Rich

Categories