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.
Related
I have hibernate XML where I'd like to retrieve a row as an instance of the subclass based on the results of a discriminator formula. Trouble is, the field to examine is on a referenced table, not on the table itself. My object is a merchant-user account, which is a combination of user account and merchant. The user_account table has a role field that may include several values, of which a few indicate a level of admin permissions. I'd like to do something like this, but this itself (trying to reference userAccount.role in the case statement) does not work:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.rc.model.mexp.MerchantUserAccount" table="merchant_user_account">
<id name="merchantUserAcctId" type="int">
<column name="merchantuseracctid" scale="10" precision="0" not-null="true" unique="true" sql-type="int unsigned"/>
<generator class="com.rc.model.jdbc.sequence.MexpIdentifierGenerator">
<param name="sequence">seq_merchantuseraccountid</param>
<param name="idDataType">int</param>
</generator>
</id>
<discriminator formula="case when userAccount.role in (2, 3, 4, 5) then '1' else '0' end" type="boolean"/>
<many-to-one name="userAccount" class="com.rc.model.user.security.UserAccount" column="userid"
unique="true" not-null="true" cascade="all"/>
<many-to-one name="merchantAccount" class="com.rc.model.mexp.MerchantAccount" column="merchantacctid"
not-null="true" cascade="all"/>
<subclass name="com.rc.model.mexp.AdminMerchantUserAccount" discriminator-value="true"/>
</class>
</hibernate-mapping>
Is this even possible?
I'm just learning how to use Hibernate for database modeling/mapping. I'm using the following code snippet to get a User model.
Session session = HibernateUtil.getSessionFactory().openSession();
User user = (User) session.get(User.class, id);
session.close();
The user's configuration file looks like this:
<?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="c3.data.User">
<id name="id" type="java.lang.Integer">
<generator class="identity"/>
</id>
<property name="username" type="java.lang.String"/>
<property name="password" type="java.lang.String"/>
<property name="email" type="java.lang.String"/>
<property name="salt" type="java.lang.String"/>
<one-to-one name="authSession" property-ref="user" class="c3.data.AuthSession"/>
<set name="projects" inverse="true">
<key column="projectLeadId"/>
<one-to-many class="c3.data.Project"/>
</set>
</class>
</hibernate-mapping>
As you can see, it holds a relationship to other models called AuthSession and Project. I can post their configuration files if necessary.
The problem I'm having is when I call Session.get(), it never returns; it just hangs. Hibernate shows me the queries, and it runs the query for the User, the AuthSession, and the Projects. Apart from not confirming the values of the parameters, the queries look correct. After the queries for the Projects, nothing happens. I put a print statement right after the call for get and it never gets printed.
I don't know what else to check to see what might be going wrong. Any thoughts?
Edit:
There was an exception, here's the stack trace:
java.lang.NullPointerException
at org.hibernate.persister.entity.AbstractEntityPersister.loadByUniqueKey(AbstractEntityPersister.java:2314)
at org.hibernate.type.EntityType.loadByUniqueKey(EntityType.java:664)
at org.hibernate.type.EntityType.resolve(EntityType.java:444)
at org.hibernate.engine.internal.TwoPhaseLoad.doInitializeEntity(TwoPhaseLoad.java:168)
at org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:134)
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:999)
at org.hibernate.loader.Loader.doQuery(Loader.java:878)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:293)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:263)
at org.hibernate.loader.Loader.loadCollection(Loader.java:2094)
at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:61)
at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:678)
at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:82)
at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:1801)
at org.hibernate.collection.internal.AbstractPersistentCollection.forceInitialization(AbstractPersistentCollection.java:661)
at org.hibernate.engine.internal.StatefulPersistenceContext.initializeNonLazyCollections(StatefulPersistenceContext.java:1014)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:298)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:263)
at org.hibernate.loader.Loader.loadEntity(Loader.java:1977)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:82)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:72)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3821)
at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:458)
at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:427)
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:204)
at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:260)
at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:148)
at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1075)
at org.hibernate.internal.SessionImpl.access$2000(SessionImpl.java:175)
at org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.load(SessionImpl.java:2421)
at org.hibernate.internal.SessionImpl.get(SessionImpl.java:971)
at c3.data.UserManager.getUser(UserManager.java:22)
at c3.data.AuthSessionManager.sessionExists(AuthSessionManager.java:14)
at c3.console.Server.logon(Server.java:289)
at c3.console.Server.onToken(Server.java:242)
at tokenwebsocket.server.TokenWebSocketServer.onMessage(TokenWebSocketServer.java:37)
at org.java_websocket.server.WebSocketServer.onWebsocketMessage(WebSocketServer.java:457)
at org.java_websocket.WebSocketImpl.deliverMessage(WebSocketImpl.java:561)
at org.java_websocket.WebSocketImpl.decodeFrames(WebSocketImpl.java:328)
at org.java_websocket.WebSocketImpl.decode(WebSocketImpl.java:149)
at org.java_websocket.server.WebSocketServer$WebSocketWorker.run(WebSocketServer.java:593)
I have a parent-child (one-to-many) relationship between product & packages. one product can have multiple packages. In my 'Edit Product' jsp page I am allowing user to edit product & delete/add/edit any package. Now, on submitting the changes I am doing saveOrUpdate(product) in my controller method. What I am observing is that new packages are getting added in DB but the ones that are deleted by user hence not in the packages Set of Product are not getting deleted from DB. My hiberate configurations snippet is below. Am I missing something here?
<hibernate-mapping>
<class name="author.vo.ProductVO" table="Product">
<id name="ProductID" type="long">
<column name="Product_ID" />
<generator class="increment" />
</id>
<set name="packages" inverse="true" cascade="all" lazy="false">
<key column="Product_ID" not-null="true" on-delete="cascade"/>
<one-to-many class="author.vo.PackageVO" />
</set>
Code for adding product
#Autowired
private HibernateTemplate hibernateTemplate;
this.hibernateTemplate.saveOrUpdate(prod);
You should add "cascade delete orphan" to your xml config, also, why dont you try a simple situation first of all.
is a save working with a parent - child.
is a delete working with a parent - child.
I'm starting my adventure with Hibernate, so please be patient :)
I want to make mapping for two tables, for example A and B. The relation beetwen A and B is one-to-many.
I wrote this hbm.xml file:
<hibernate-mapping package="something">
<class name="A" table="A">
<id name="id" type="int" column="ID">
<generator class="native" />
</id>
<set name="setInA" sort="natural" cascade="all" lazy="false">
<key column="ANOTHER_ID"/>
<one-to-many class="B" />
</set>
</class>
<class name="B" table="B">
<id name="anotherId" type="int" column="ANOTHER_ID">
<generator class="native" />
</id>
</class>
</hibernate-mapping>
Of course I made also POJO classes A and B.
And now, when I try to do:
A a = new A();
Set<B> set = new TreeSet<B>();
set.add(new B());
a.setSetInA(set);
session.save(a);
Hibernate inserts new row to table A, but (what is the worst) is not inserting new row to B table, but only makes SQL Update on not existing row in B.
Can tell me anyone why it is happening? What I made wrong?
You should either persist B's objects firstly, or use Cascade option.
You can use Cascade without using annotations:
<set name="setInA" sort="natural" cascade="all" lazy="false" cascade="all">
<key column="ANOTHER_ID"/>
<one-to-many class="B" />
</set>
This will ensure that collection of B instances is inserted when you insert A.
Found this question while searching for causes of the same symptoms in my system. cascade="all" did not help.
In my case I solved this by adding a mapping to the list element, in this example class B.
Please note that the enclosing class (A in this example) also was versioned. Hibernate might require that versioning (used for optimistic locking) must be enabled for all nested elements. I haven't found any documentation to support this theory.
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