I am facing a strange problem and am unable to find a solution. I am trying to load a set of objects in Java using Hibernate from MySQl db.
These is a simplified version of my hibernate mappings and code:
<class name="org.Foo.Class1" table="class_profile" >
<cache usage="read-write"/>
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="amount" column="amount"/>
</class>
<class name="org.Foo.Class2" table="class_profile" >
<cache usage="read-write"/>
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="amount" column="amount"/>
</class>
This is my code to access, the objects:
public List<Class1> loadProfiles(final List<Integer> pIds)
{
return (List<Class1>)getHibernateTemplate().executeFind(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
return session.createQuery("from Class1 il where il.id in (:idList)")
.setParameterList("idList", pIds)
.list();
}
});
}
Now, when I run my code
List<Class1> profiles = fooService.loadProfiles(Arrays.asList(3,4));
I get FOUR objects (instead of 2) in the list profiles - TWO Class1 objects and TWO Class2 objects. Where are the TWO Class2 objects coming from?
When you have inheritance between two entities, Hibernate needs to be able to tell which class a particular database row is supposed to represent. You do this by adding a discriminator class to the table that tells it which class to build to represent the row.
Class2 needs to be declared using a <subclass/> element nested inside Class1 that specifies what value of what column is used to tell them apart.
Detials here: http://docs.jboss.org/hibernate/orm/3.5/reference/en/html/inheritance.html#inheritance-tableperclass
Related
I have working on a RESTful application (Jersey) using json (with Gson, from Google) for get and send the data. For persistence, I'm using Hibernate. In next lines, I will put some code as an example (it's not the real data!).
I have a relation between two classes, as follows:
public class A {
private Long id;
private B fieldB1;
private B fieldB2;
}
(It is assumed that the class has getters and setters.)
The mapping file is as follows:
<hibernate-mapping>
<class name="com.app.A" table="A">
<id name="id" type="java.lang.Long">
<column name="ID" unique="true"/>
<generator class="identity" />
</id>
<many-to-one name="fieldB1" class="com.app.B" fetch="join">
<column name="FIELDB1" />
</many-to-one>
<many-to-one name="fieldB2" class="com.app.B" fetch="join">
<column name="FIELDB2" />
</many-to-one>
</class>
</hibernate-mapping>
When I try to send a request to the REST service in the server (now i'm using Postman from Google) in order to get an A object by id, converted to JSON, the following 4 scenarios occur:
If fieldB1 and fieldB2 are null, the json object I receive doesn't have fieldB1 and fieldB2 in the body.
If only fieldB2 is null, the json object I receive has fieldB1 but doesn't have fieldB2 in the body.
If fieldB1 and fieldB2 have data (that is, both are not null), I obtain this exception: java.lang.UnsupportedOperationException:
Attempted to serialize java.lang.Class:
org.hibernate.proxy.HibernateProxy. Forgot to register a type
adapter?
If fieldB1 is null but fieldB2 are not null, item n° 3 occurs.
As I see, the problem appears with the second class B in the mapping. So?
I'm working with Hibernate 4.3.5, Java 1.6 and Spring 4.0.3.
I've mapped the entities through hbm, and I want my application works with logical deletion.
So, in each mapped entity, I've added a property named 'deleted', which indicates if an entity is deleted or not.
Because I don't want to load the deleted entities (the ones having true the deleted property), I've used the where clause in the mapped classes, so I only get the entities aren't logically deleted.
And also, I've added the same where clause to every one-to-many relationship.
In one particular case, I've got a Report entity that has a one-to-many relationship with the Document entity.
So, when I mark a Document as deleted, and I save the Report entity (with merge), I expect than the Report entity doesn't keep the Document marked as deleted. But this doesn't occur.
This is the hbm for the Report entity:
<hibernate-mapping>
<class
name="es.entities.Report"
table="reports"
dynamic-insert="false"
dynamic-update="false"
where="deleted = 0">
<id name="id">
<generator class="identity"/>
</id>
<property name="title"></property>
<property name="deleted"></property>
<set
name="documents"
table="documents"
cascade="all"
lazy="false"
where="deleted=0">
<key column="id_report"/>
<one-to-many class="es.entities.Document"/>
</set>
</class>
</hibernate-mapping>
Here it is the hbm for the Document entity:
<hibernate-mapping>
<class
name="es.entities.Document"
table="documents"
dynamic-insert="false"
dynamic-update="false"
where="deleted = 0">
<id name="id">
<generator class="identity"/>
</id>
<property name="name"></property>
<property name="type"></property>
<property name="size"></property>
<property name="deleted"></property>
</class>
</hibernate-mapping>
I use a Service (ReportService) to open a Spring transaction. The method is:
#Autowired
private ReportDao reportDao;
#Transactional
public Report save(Report report) {
this.reportDao.save(report);
}
And this is the DAO (ReportDao) method I use to save the Report entity:
public Report save(Report report) {
return (Report) this.currentSession().merge(report);
}
I put an example:
The parameter I send to the service contains a Report object, with two Document objects, one of them deleted and the other not.
The DAO method returns the same information, but I'd like this method returns only the documents are not deleted.
Note: if I use another method with another transaction, I obtain the report only with the document is not deleted, but I'd like to do this in the same transaction.
Can anybody help me or show me an alternate to this? It is possible to use other Session method than merge?
Thanks a lot.
Merge method create a copy from the passed entity object and return it. Try re-fetching the report entity post merge.
I am working with a environment using Hibernate 3.6. I am trying to implement a MySQL table that uses UUIDs from java.util as the PK, these UUIDs are generated from multiple Strings defined by the EntityImage. Here is the code I am using to generate those keys.
public class EntityIDGenerator implements IdentifierGenerator {
...
private UUID generateUUID(String str) {
return UUID.nameUUIDFromBytes(str.getBytes());
}
public UUID generateUUID(EntityImage image) {
return generateUUID(image.getEntityId()+image.getEntityType().toUpperCase()+image.getFilename().toUpperCase());
}
...
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
if(object instanceof EntityImage) {
return generateUUID((EntityImage)object);
}
return null;
}
}
and here his my hibernate
<class name="EntityImage" table="entity_image">
<id name="id" column="id" type="entityUUID" unsaved-value="null">
<generator class="com.covelo.energy.score.db.EntityIDGenerator" />
</id>
<property name="entityId" type="int"/>
<property name="entityType" type="string" length="45" />
<property name="filename" type="string" length="90" />
<property name="fileType" type="string" length="5"/>
<property name="imageData" type="binary" column="image_data"/>
</class>
With my current setup I can only get it to work one way, generate the UUID myself and check if a key exists in the Hibernate then call a save or update accordingly (or just set the UUID to null or the value respectively)
This way seems wrong especially for having this PK generator implemented. Ideally Hibernate would generate a PK using my IdentifierGenerator then check if it exists and update or save it. Am I going about my architecture wrong or is there a method to do this in hibernate?
Note: I am well aware of how saveOrUpdate works Hibernate documentation
-Thanks in advance.
Ned
i am trying to load a hibernate object ForumMessage but in it contain another object Users and the Users object is not being loaded.
My ForumMessage Mapping File:
<?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 Jan 4, 2011 10:10:29 AM by Hibernate Tools 3.4.0.Beta1 -->
<hibernate-mapping>
<class name="com.BiddingSystem.Models.ForumMessage" table="FORUMMESSAGE">
<id name="ForumMessageId" type="long">
<column name="FORUMMESSAGEID" />
<generator class="native" />
</id>
<property name="ForumMessage" type="java.lang.String">
<column name="FORUMMESSAGE" />
</property>
<many-to-one name="User" class="com.BiddingSystem.Models.Users" fetch="join">
<column name="UserId" />
</many-to-one>
<property name="DatePosted" type="java.util.Date">
<column name="DATEPOSTED" />
</property>
<many-to-one name="Topic" class="com.BiddingSystem.Models.ForumTopic" fetch="join">
<column name="TopicId" />
</many-to-one>
</class>
</hibernate-mapping>
and I am using the follwing code:
Session session = gileadHibernateUtil.getSessionFactory().openSession();
SQL="from ForumMessage";
System.out.println(SQL);
Query query=session.createQuery(SQL);
System.out.println(query.list().size());
return new LinkedList <ForumMessage>(query.list());
<many-to-one name="User" class="com.BiddingSystem.Models.Users" fetch="join" lazy="false">
You need to add lazy="false" as well.
You can add lazy="false" to the many-to-one mapping which will load the users when the ForumMessage is loaded. Alternatively you could initialize the users list using Hibernate.initialize(). Just make sure you do this before you close the session.
Session session = gileadHibernateUtil.getSessionFactory().openSession();
string sql = "from ForumMessage";
Query query = session.createQuery(sql);
List results = query.list()
for(ForumMessage message : results)
{
Hibernate.initialize(message.User);
}
return new LinkedList <ForumMessage>(results);
You should only do one of these though if you have a need to. Hibernate by default lazy loads objects to avoid unnecessary calls to the database. For example:
public LinkedList getMessages()
{
//It's assumed the session is opened and closed elsewhere.
string sql = "from ForumMessage";
Query query = session.createQuery(sql);
List results = query.list();
//The overhead of extra calls to the database occur here.
//This would have a similar impact if lazy load is set to false.
for(ForumMessage message : results)
{
Hibernate.initialize(message.User);
}
return new LinkedList <ForumMessage>(results);
}
public void printMessages()
{
LinkedList messages = getMessages();
for(ForumMessage message : messages)
{
System.out.println(message.ForumMessage);
}
}
In the above code sample the overhead is incurred for loading all the Users objects but those objects are never used. If Hibernate's lazy-loading were used then this extra overhead would not be incurred. In the following example the list of users isn't loaded until the list is used. This way calls are not made to the database until the data is actually needed.
public LinkedList getMessages()
{
//It's assumed the session is opened and closed elsewhere.
string sql = "from ForumMessage";
Query query = session.createQuery(sql);
List results = query.list();
return new LinkedList <ForumMessage>(results);
}
public void printMessages()
{
LinkedList messages = getMessages();
for(ForumMessage message : messages)
{
//Hibernate will load the users objects here when they are accessed.
for(Users user : message.User)
{
System.out.println(user);
}
}
}
One point to be careful of when lazy loading is all loading must be done in an active session. If you don't have an active session and you try and access something that has not yet been loaded Hibernate will throw a LazyInitializationException.
In addition, using Hibernate's lazy load functionality complies more with the idea of persistence ignorance where as using Hibernate.initialize() does not.
I have seen posts all over the internet that talk about how to fix the TransientObjectExceptions during save/update/delete but I am having this problem when calling list on my Criteria.
I have two objects A and B. A has a field named b which is of type B. In my mapping b is mapped as a many-to-one. This all runs in a larger persistence framework (the framework is kind of like Core Data) and so I don't use any cascades in my hibernate mappings since cascades are handled at a higher level.
This is the interesting code surrounding my criteria:
A a = new A();
B b = new B();
a.setB(b);
session.save("B", b); // Actually handled by the higher level
session.save("A", a); // framework, this is just for clarity
// transaction committed and session closed
...
// new session opened
Criteria criteria = session.createCriteria(A.class);
criteria.add(Restrictions.eq("b", b));
List<?> objects = criteria.list();
Basically I am looking for all objects of type A such that A.b equals a particular instance of b (I actually tried restructuring a query so that I was passing in the id of b just to make sure that b wasn't causing me problems).
Here is the stack trace that occurs when I call criteria.list():
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: B
at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:244)
at org.hibernate.type.EntityType.getIdentifier(EntityType.java:449)
at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:141)
at org.hibernate.loader.Loader.bindPositionalParameters(Loader.java:1769)
at org.hibernate.loader.Loader.bindParameterValues(Loader.java:1740)
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1612)
at org.hibernate.loader.Loader.doQuery(Loader.java:717)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:270)
at org.hibernate.loader.Loader.doList(Loader.java:2294)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2172)
at org.hibernate.loader.Loader.list(Loader.java:2167)
at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:119)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1706)
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:347)
Here is my mapping:
<class entity-name="A" lazy="false">
<tuplizer entity-mode="dynamic-map" class="MyTuplizer" />
<id type="long" column="id">
<generator class="native" />
</id>
<many-to-one name="b" entity-name="B" column="b_id" lazy="false" />
</class>
<class entity-name="B" lazy="false">
<tuplizer entity-mode="dynamic-map" class="MyTuplizer" />
<id type="long" column="id">
<generator class="native" />
</id>
</class>
Can anyone help me figure out why I would be getting a TransientObjectException during a fetch? Preferably I would like to find a solution that does not rely on cascades since they tend to mask problems that occur in the higher level framework.
The problem is that b was made persistent in another session, which is closed and the query is created in a new session. When a session is closed, all objects in its persistence context become detached. If you want to later reuse them in another session, you need to re-attach them to that session first:
session.update(b);
Quote from the Hibernate book:
The update() operation
on the Session reattaches the detached object to the persistence context and
schedules an SQL UDPATE. Hibernate must assume that the client modified the
object while it was detached. (Otherwise, if you’re certain that it hasn’t been modified,
a lock() would be sufficient.) The persistence context is flushed automatically
when the second transaction in the conversation commits, and any
modifications to the once detached and now persistent object are synchronized
with the database.
The saveOrUpdate() method is in practice more useful than update(),
save(), or lock(): In complex conversations, you don’t know if the item is in
detached state or if it’s new and transient and must be saved. The automatic
state-detection provided by saveOrUpdate() becomes even more useful when you
not only work with single instances, but also want to reattach or persist a network
of connected objects and apply cascading options.
Note that there is also a merge() method, for cases when the same entity has been loaded into the new persistence context before the older detached instance could be re-attached. In this case, you have two physically distinct instances representing the same entity, thus they should be merged to avoid a NonUniqueObjectException.
Another easy way accomplish the same is to use the attribute cascade=all on the collection mapping of child class from within the parent class mapping. Here's how the mapping looks
<class entity-name="A" lazy="false">
<tuplizer entity-mode="dynamic-map" class="MyTuplizer" />
<id type="long" column="id">
<generator class="native" />
</id>
<many-to-one name="b" entity-name="B" column="b_id" lazy="false" cascade="all" />
</class>
<class entity-name="B" lazy="false">
<tuplizer entity-mode="dynamic-map" class="MyTuplizer" />
<id type="long" column="id">
<generator class="native" />
</id>
</class>
Since you have insert data such as in persist(),save() in hibernate..But the above error is you have just do merge() and others method which can perform the update information.