Java Spring-Hibernate mapping question? - java

Say I have domain objects corresponding to Posts and Users. Nevertheless, I have corresponding database tables containing information relevant to "posts" and "users".
Currently I have properly set up the mapping in Hibernate so that I can pull the info from the "posts" table and save it as a Post object. However, I then added to my Posts domain object, a reference to a User object (so that each post can have a corresponding User object).
In my database structure, the "posts" table has a user_id column which is a foreign key into the "users" table.
My question is: when querying the "posts" table in my DAO, do I have to join on the "users" table and then somehow cast the returned user data into a User object? Or can I simply leave my query as it is (i.e. just querying the "posts" table), and somehow add a hibernate mapping so that the User attribute in the Posts table gets populated? (I guess I am wondering if Hibernate can automatically generate the join for me if I set up the mapping properly - examples would be great too!)
Thanks, and I hope I was clear enough.
Update: Just to clarify, here are some code snippets:
My Post Object:
public class Posts {
private String title;
...
private User user;
//getters and setters here
}
My Post table columns:
post_id (primary key)
title
...
user_id (foreign key into User table)
My mapping (without taking into account the User attribute) currently looks like this:
<class name="com...Post" table="post">
<id name="pId" column="post_id" />
<property name="title" column="title" type="java.lang.String" />
...
<!-- Need to add mapping here to join with user table?? -->
</class>
So basically, my DAO currently fetches a Post object without the private User user attribute (since I just added this). My question was how do I populate that attribute in the Post object (so that the fetched Post object also contains a User object)?
Sorry if the current posts already answered this...they were just slightly confusing to me..

If I understand your question correctly, I believe you're looking for the many-to-one mapping (many Posts to one User). Add the following to your mapping for the Post object:
<many-to-one name="user" class="User" column="user_id" lazy="false" />

Update: Well, you got a confusing answer first because you asked a confusing question... The answer to your renewed question is indeed to define a many to one mapping for your Post class (as others have already mentioned). Now, if you want to fetch the whole stuff with a single join query, you write:
<many-to-one name="user" class="User" column="user_id" fetch="join" />
Original post:
By default, Hibernate fetches lazily. In fact, you need to touch the lazy property only if you want eager fetching. Rough example for the behaviour of the default lazy fetch plan:
Post post = (Post) session.load(Post.class, new Long(123));
// at this point, post refers to a proxy object created by Hibernate
// in the background - no post or user data has been loaded from DB
post.getId();
// post still refers to the proxy object
User user = post.getUser();
// post is now loaded, but user not - it refers to a proxy object
String name = user.getName(); // Now the user data is loaded from DB
So if you are happy with multiple queries, you don't need to do anything special. OTOH if you want to fetch all post and user data in a join query, you need to set the attribute fetch="join" in your mapping for theuser` property.

You can do that by using the lazy property which is not activated by default.
See some examples here http://www.javalobby.org/java/forums/t20533.html

Related

Map complex value objects in Hibernate

I've already asked this question on the Hibernate's forum, but I thought I'd ask it here too.
I'm trying to map the following model while preserving the value semantics of the TranslatedText and Translation value objects:
Both values as dependent objects
Ideally I'd map TranslatedText as a <component> within Question and Translation as a <bag> of <composite-element> within TranslatedText.
It would have been simple to map if Question was only referencing one TranslatedText, but since it references two I need some kind of discriminator based on the name of the property holding the value (title or description) in order to map the Translation with a foreing key composed of (question_id,property_name,language_code).
One problem with that is that the propertyName isin't part of the model and shouldn't, but I haven't found a way to force Hibernate to insert a value that doesn't come from the model.
Therefore, I tried to change the model and introduce specialized Title and Description classes so that I'd have a type in there that I could use as a discriminator.
At the end that did not really help much:
<component name="title" class="TranslatedText">
<bag name="translations" table="Translation">
<key>
<!-- PROBLEM: Could not find a way to create a custom join expression on question.id and question.title.type in here. -->
</key>
<composite-element class="Translation">
<!-- PROBLEM: Could not found a way to make Hibernate insert title.type from here, without having this value on the Translation object. -->
<property name="languageCode" type="string" column="language_code"/>
<property name="text" type="string"/>
</composite-element>
</bag>
</component>
TranslatedText with <many-to-one>
I managed to get something close to what I need by mapping TranslatedText as an entity within Question using a <many-to-one> and then map Translation as a collection of values within TranslatedText, but the main problem with that approach is that there is no easy way to get rid of the orphaned TranslatedText and Translation. I'd either have to do this with a DB trigger or a scheduled process.
Conclusion
At this point I'm under the impression that Hibernate is not flexible enough to map the initial model with the proper semantics, but hopefully I'm wrong and there is a way to do it.
I have not found a way to map them as values. However the next solution works and it might be helpful for you. I removed TranslatedText and linked Question directly with collection of Translation.
#Entity
public class Question {
#Id
private String id;
#JoinTable
#OrderColumn
#OneToMany(fetch = EAGER, cascade = ALL)
private List<Translation> titleTranslations;
#JoinTable
#OrderColumn
#OneToMany(fetch = EAGER, cascade = ALL)
private List<Translation> descriptionTranslations;
}
The drawback here is that Translation has to be Entity class.
#Entity
public class Translation {
#Id
private String id;
private String languageCode;
private String text;
}

Relational Data is Not Fetching in Ebean

Background
I am using Play Framework(Java) to store data. Play Framework uses Ebean to convert classes into data that can be stored in a a database.
Issue
I am currently having trouble fetching relational data completely. I have a User and a UserEmail model. The User can own multiple UserEmails.
User Model Code
User Email Model Code
When I try and fetch a User, the User data is fetched correctly, however the UserEmails are not.
Fetching Code
When I specifically add fetch("emails") to the fetch code
User.find.fetch("emails").where().eq("userId", testUserId).findUnique();
It seams like it at least gets the emails. However when I try and show them via
return ok(Json.toJson(retrievedTestUser));
I get This Error
Question
Is there some way I can make Play Framework/Ebean automatically fetch the emails without adding fetch("emails") to every query? Maybe an annotation?
How do I resolve the error above? I get why it is thrown, but how can I fix it? Is there any way to have it only fetch one level deep?
I have found solution to above problem. Every entity should have id.
User class has field annotated with #Id but UserEmail has not.
After adding #Id annotation above email field of UserEmail class user is fetched properly and its email list is not empty.
#Id
public String email;
One more thing that I spotted in your code:
When you are creating bidirectional relation then you should use mappedBy attribute on one side to indicate that these are two ends of one relation and not two separate relations. So there should be:
#OneToMany(mappedBy="user", cascade = CascadeType.ALL)
#Constraints.Required
public List<UserEmail> emails;

Hibernate filter Entity where oneToMany relation contains an object

I want to use the hibernate filter but I don't know if what I want to do is possible
I have 2 entities :
Message and MessageUser.
A Message has a list of MessageUser.
I want to create a filter so I can do something like :
final Session filteredSession = sessionFactory.openSession();
final Filter filter = filteredSession.enableFilter("userRecipient");
filter.setParameter("userRecipient", myUser);
filter.validate();
final List<Message> userMessages = filteredSession.createQuery("from Message").list();
it returns me only the message where myUser is the recipient ?
is it possible to to and how?
Thanks a lot !
If you are comfortable with Criteria you could create criteria like this
Session hbSession= sessionFactory.openSession();
Criteria criteria = hbSession.createCriteria(Message.class);
criteria.createCriteria("msgUserList","userListAlias");// msgUserList is variable name of users list in Message
criteria.add(Restrictions.eq("userListAlias.user",myUser));//user is variable for User type in msgUserList's class.
List<Message> userMessages = criteria.list();
Have a look at this for reference while creating criteria!
If you only want to use filter then I hope you have configured filter on your User List some thing like bellow
By *.hbm.xml
<hibernate-mapping package="com....">
<class name="Message" table="message_table">
....
<list name="msgUserList" inverse="true" cascade="all">
<key column="user_id" />
<one-to-many class="MessageUsers" />
<filter name="userRecipient" condition="user_id =:userParam" />
</list>
</class>
<filter-def name="userRecipient">
<filter-param name="userParam" type="User" />//User is class
</filter-def>
</hibernate-mapping>
Or By annotation
#Entity
#FilterDef(name="userRecipient",
parameters=#ParamDef(name="userParam", type="PAKAGE.User" ))
#Table(name = "message_table", catalog = "your_db")
public class Message{
...
#OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
#Filter(name = "userRecipient",condition="user = :userParam")
public List<MessageUser> msgUserList;
after this you will be able get your filter working
Filter filter = session.enableFilter("userRecipient");
filter.setParameter("userParam", myUser);
Update
Purpose of Filter is different than the Criteria, from my understanding you can say that Filter is just like a Criteria that is already applied on your class or collection which has on and off switch. If your hibernate session has certain filter enabled with it's parameters set than that filter is on and all queries relating to the class or collection which has this filter specified will always return filtered result as per the condition. This means you don't have to explicitly define it every time and by using getEnabledFilter("filterName") you can just change that filter's parameters any time.
Example usage of filter can be if you have Movies table and Actor table with many-to-many relationship, like Leonardo Dicaprio can have many movies at the same times A titanic can have many actors, here when you get Actor obviously you would want only those movies which this Actor has performed in, so you can use filter here which is applied on collection of Movies that is mapped in Actor class. This way when you get Actor object say by simple criteria of name and nothing else and access it's Movie collection by . operator on Actor object it will return you only movies which that actor has performed. This also means no matter how you got Actor object from database when you access Movie collection of Actor it will provide you movies that this actor has performed in
Criteria on the other hand you can use when you require result from database with certain conditions which does not need to be replicated rather you don't want it to be replicated later in the hibernate session. Like Actor lets say Leonardo Dicaprio containing collection of Movies that got nominated him in Oscar. This collection will only be populated in Actor object when gone through certain criteria and will not be available on other Actor objects which have not being retrieved by this criteria.
I hope you understood basic concept of filter and criteria, and from my understanding of your problem it will be better if you use criteria!

Querying "extension tables" using Hibernate

I am having a querying issue in Hibernate. I have a table, 'test', with existing data. I have a requirement where I can not modify the schema of the test table, so I created another table, 'testExtension', whose primary key is a foreign key to the primary key of Test. Data in testExtension is a subset of the data in test. i.e. There will be less rows in 'testExtension' than in 'test'.
I have defined this relationship in a configuration file as follows:
<class name="Test" table="test">
<id name="testId" column="test_id">
<generator class="sequence">
<param name="sequence">test_id_seq</param>
</generator>
</id>
<property name="name"/>
<joined-subclass name="TestExtension" table="testExtension">
<key column="test_id"/>
<property name="summary" />
<property name="homepage"/>
</joined-subclass>
With this setup, I am able to create a TestExtension object in my Java program, populate it with data, 'save' it via Hibernate, and commit the transaction. And it correctly saves data in both Test and TestExtension.
My problem is occurring when I am trying to query data from these tables. Right now if I query for a particular test_id using the TestExtension.class to QBE, it will only return a row if that id exists in both Test and TestExtension. If I use the Test.class to QBE, it will return the row but I will not have access to any of the data stored in TestExtension.
My question is: how can I query these tables so that the results are based off a 'left outer join' of both Test and TestExtension? Any solution is appreciated, whether it's query by example, HQL, or something else (though preferably not raw SQL).
Thanks!
HQL is probably the easiest way to do this. Docs are here:
http://docs.jboss.org/hibernate/stable/core/reference/en/html/queryhql-joins.html
Sounds like what you might want to do is remap your relationships so that Test and TestExtension use a one-to-one relationship instead of inheritance. Then you can query for Test and TestExtension using a left outer join across the one-to-one.
If you use HQL to write a query for the Test class, it should do what you want. I assume QBE is effectively adding the class of your example entity as one of the query parameters.
So sth like:
from Test t where t.property = :value
should return either Test or TestExtension entities. Note that (at least with the versions of Hibernate I've used). In this case, Hibernate should immediately give you back the actual entities rather than a proxy too--- be aware that TestExtension entities can sometimes be returned as plain Test lazy-loading proxies.

Audit Schema Mapping with Hibernate and AOP

I am trying to audit the action that the user performed that resulted in changes to corresponding tables. For example if a user were to transfer money between 2 accounts this would generate the following sequence of events:
Insert transfer amount into Transfer table
Subtract transfer amount from balance in Balance Table for Account 1.
Add transfer amount to balance in Balance Table for Account 2.
The parent audit message for all tables would be: "User generated transfer for amount XXX"
This is achieved with the following schema:
schema
alt text http://img48.imageshack.us/img48/7460/auditloggingiv6.png
The question is how do I represent this in hibernate?
I have created the following:
In Balance and Transfer's mapping files
<set name="auditRecords" table="TransferAuditRecord" inverse="false" cascade="save-update">
<key>
<column name="AuditRecordID" not-null="true" />
</key>
<one-to-many class="audit.AuditRecord"/>
</set>
Transfer and Balance classes then implement IAuditable which has methods
public void setAuditRecords(Set<AuditRecord> auditRecord);
public Set<AuditRecord> getAuditRecords();
In AuditRecord's mapping file I have:
<many-to-one name="parentAuditRecord" lazy="false"
column="parent_id"
class="audit.AuditRecord"
cascade="all" />
Then in Logging class using AOP and Hibernate Interceptors I have:
AuditRecord auditRecord = new AuditRecord();
auditRecord.setUser(userDAO.findById(
org.springframework.security.context.SecurityContextHolder.getContext()
.getAuthentication().getName()));
auditRecord.setParentAuditRecord(getCurrentActiveServiceRecord());
auditable.getAuditRecords().add(auditRecord);
Then in the Service Class I call the following method, enclosed in a transaction:
save(balance1);
save(balance2);
transfer.setPassed(true);
update(transfer);
The parentAuditRecord is created using AOP with a thread safe stack, and the AuditRecordType_id is set using annotations on the method.
I left out the "passed" column on the transfer table. Previously I call save(transfer) to insert the transfer amount into the Transfer table with passed set to false. (This action is also audited).
My requirements are slightly more complicated than the example above :P
So the sequence of events for the above should be:
Update Transfer Table
Insert into AuditRecord (Parent)
Insert into AuditRecord (Child)
Insert into TransferAuditRecord
Insert into Balance Table
Insert into AuditRecord (Child)
Insert into BalanceAuditRecord
Insert into Balance Table
Insert into AuditRecord (Child)
Insert into BalanceAuditRecord
However the cascade options defined above fail at the update statement. Hibernate refuses to insert a record into the many-to-many table (even if unsaved-value="any" on the AuditRecord Mapping). I always want to insert rows into the many-to-many tables so potentially one Transfer has many Audit Records marking the previous events. However, the latest event determines the message the user wants to see. Hibernate either tries to update the many-to-many table and previous AuditRecord entries or it simply refuses to insert into AuditRecord and TransferAuditRecord, throwing a TransientObjectException.
The Audit Message is retrieved something like this:
msg=... + ((AuditRecord) balance.getAuditRecords().toArray()[getAuditRecords().size()-1])
.getParentAuditRecord().getAuditRecordType().getDescription() + ...;
The message should say something like this:
"Username set transfer to passed at 12:00 11-Oct-2008"
EDIT I decided to go with explicitly mapping the many-to-many table (with an associated interface), and then in afterTransactionCompletion, calling save on the parent audit record (which cascades the save to the child audit records) then explicitly saving the interface on all child mapping tables. This isn't a true audit history, rather a non-invasive method of recording user action. I will look into Envers if I need more complete audit history at a later point.
Seems like the relationship between parentAuditRecord and transferauditrecord and balance auditrecord shouldn't be one to many. When I read what you typed I'm seeing it as a table per subclass usage of that audit hierarchy which is a one-to-one relationship.
http://www.hibernate.org/hib_docs/reference/en/html/inheritance.html
You may also want to check out JBoss's Envers project.
At the design level, it seems like a insert only db design would work marvels here.
If you want to keep it the way it is right now (which I'm sure you do), you could look into Hibernate listeners/interceptors/events (well defined in the doc: http://www.hibernate.org/hib_docs/v3/reference/en-US/html_single/)
Else, I just looked into JBoss Envers and it also seems pretty useful.

Categories

Resources