I'm currently working on a Quizzing Tool that uses hibernate and spring. I'm actually building it as a Sakai LMS tool and that complicates this question a little more, but let me see if I can generalize.
My current scenario is when users go to a StartQuiz page, which when they submit the form on the page, initializes an Attempt object(Stored by hibernate). It populates the object below:
<class name="org.quiztool.model.Attempt" table="QT_ATTEMPTS">
<cache usage="transactional" />
<id name="id" type="long">
<generator class="native">
<param name="sequence">QT_ATTEMPTS_ID_SEQ</param>
</generator>
</id>
<many-to-one name="quizId" class="org.quiztool.model.Quiz" cascade="none" />
<property name="score" type="int" not-null="true" />
<property name="outOf" type="int" not-null="true" />
<list name="responses" cascade="none" table="QT_RESPONSES" lazy="false">
<key column="id"/>
<index column="idxr"/>
<many-to-many class="org.quiztool.model.QuizAnswer" />
</list>
<list name="questionList" cascade="none" table="QT_ATTEMPT_QUESTIONS" lazy="false">
<key column="id"/>
<index column="idxq"/>
<many-to-many class="org.quiztool.model.QuizQuestion" />
</list>
<property name="userId" type="string" length="99" />
<property name="siteRole" type="string" length="99" />
<property name="startTime" type="java.util.Date" not-null="true" />
<property name="finishTime" type="java.util.Date" />
</class>
It randomly picks out a set of questions and sets the start time and a few other properties, then redirects the user to the TakeTheQuiz page after saving the object through hibernate.
On the TakeTheQuiz page it loads the attempt object by its ID which is passed as a request param, then prints and formats it into an html form for the user to fill out the quiz. About 2/5 concurrent users will see no questions. The attempt object loads, and its questions are empty.
My theory is that the question list in the Attempt object is either not inserting immediately to the database(which is fine as long as the object goes to the hibernate cache, and I can then get it from the cache ,which I cant see to figure out how to do) OR it is saving to the Database, but my load of the object on the TakeTheQuiz page is reading an incomplete object from the cache.
Admittedly my Hibernate knowledge is limited, so if someone can help me understand what could be happening here and how to fix it, please let me know.
The answer, as I found out, was simple. It seemed that my save function was committing to the database lazily. Once I forced commits for that object at the end of each transaction the problem was solved.
I ended up writing my own hibernate session code which looks like this:
Session session = getSession();
session.beginTransaction();
session.saveOrUpdate(attempt);
session.getTransaction.commit();
session.close();
Problem solved.
My theory is that there is something wrong with the piece of code that randomly picks the questions. Are you sure that it works? Please paste some of your code.
A second theory is that there is something wrong with your transaction boundaries. When do you flush the session? And when is your transaction committed? Give it a try and set the FlushMode on your session to ALWAYS. Does this change something?
Related
I'm mapping some entities using Hibernate 3 for my project and simply explained I've got kind of this:
Student entity (tstudent table)
UniversityStudent entity (tuniversitystudent table)
University entity (tuniversity table)
UniversityStudent extends from Student and has its own attributes, like the university itself, which is a foreign key into the tuniversitystudent table. It is also mapped like a subclass into the Student class, using a discriminator field:
<class name="mycompany.Student" table="tstudent" discriminator-value="BASIC">
<id name="id" column="id" type="integer">
<generator class="native" />
</id>
<discriminator column="type" />
<property name="name" column="name" />
<property name="surname" column="surname" />
<property name="phoneNumber" column="phone_number" />
<subclass discriminator-value="UNIVERSITY"
name="mycompany.UniversityStudent">
<join table="tuniversitystudent">
<key column="id_student" />
<many-to-one name="university" class="mycompany.University">
<column name="id_university" />
</many-to-one>
</join>
</subclass>
</class>
Well, now I want to have a Set collection with the UniversityStudent entities for each University. So I map it like that:
<class name="mycompany.University" table="tuniversity">
<id name="id" column="id" type="integer">
<generator class="native" />
</id>
<property name="name" column="name" />
<set name="universityStudents" table="tuniversitystudent">
<key>
<column name="id_university" />
</key>
<one-to-many class="mycompany.UniversityStudent" />
</set>
</class>
My problem comes when I want to load a University object, Hibernate complains that id_university doesn't exist in tstudent table. I checked the generated SQL query and it really tries to load it from tstudent.
Unknown column 'student0_.id_university' in 'field list'
It seems that it's recognizing that it is a subclass of the basic Student and tries to join the collection using a field in the parent table, but however the field is actually in the child table, because only university students can have a University assigned.
I tried another workaround which seems to work but it's not valid for me, that's mapping the UniversityStudent as a joined-subclass instead of a subclass with a join inside:
<joined-subclass name="mycompany.UniversityStudent" table="tuniversitystudent">
<key column="id_student" />
<many-to-one name="university" class="mycompany.University">
<column name="id_university" />
</many-to-one>
</joined-subclass>
However, I'm interested in keeping it as a subclass with a discriminator value. Any idea?
I checked out some resources and finally got into this bug: https://hibernate.atlassian.net/browse/HHH-1015, which looks absolutely compatible with your case. Checkout this old question as well, again very similar to your case.
I firstly read the definition of table per sublass given by Hibernate (I know, it is for version 3.3 but I couldn't find the same source for Hibernate 4): joined-subclass seems (to me) to be a custom implementation of subclass using a discriminator provided by Hibernate and that is a good reason to stay away from its usage. However, from what I know, the mappings table per sublass and table per subclass using a discriminator should be equivalent, that's why I believe the bug I pointed you out is really still open.
If you have time and will, you can try to use another JPA provider and check if you still run in the same issue. JPA 2.0 specifications is a thing, provider implementation is another! I recently run into another bug (related to #IdClass) which forced me to try EclipseLink and the configuration which was not working with Hibernate was right with Eclipse Link
Seems you can use Custom SQL (or HQL) for loading. Haven't tried it myself, but looks like, hmm, at least as a last resort, it provides a decent solution.
Define the query in your HBM:
<sql-query name="universityStudents">
<load-collection alias="unistu" role="University.universityStudents"/>
SELECT unistu.*, student.*
FROM tuniversitystudent unistu
JOIN tstudent student
ON unistu.id_student = student.id
WHERE unistu.id_university = :id
</sql-query>
And then use it inside University:
<set name="universityStudents" inverse="true">
<key/>
<one-to-many class="mycompany.UniversityStudent"/>
<loader query-ref="universityStudents"/>
</set>
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 am getting an error while generating a sequence for an id field in HSQLDB. The mapping file is configurated like this:
<hibernate-mapping>
<class name="ddol.rtdb.dto.Configuration" table="RTDB_CONFIGURATION">
<id name="id" type="int">
<column name="CONF_ID" />
<generator class="sequence">
<param name="sequence">CONF_ID_SEQ</param>
</generator>
</id>
<property generated="never" lazy="false" name="configurationDate"
type="java.util.Date">
<column name="CONF_DATE" />
</property>
It works when I use the same mapping with oracle, but does not generate the sequence when used with HSQLDB for testing. Is there anything else I should add to the mapping?
The error I am getting when I try to insert something into the table is:
ERROR org.hibernate.util.JDBCExceptionReporter - user has no privileges or object not found: CONF_ID_SEQ
HSQLDB version is 2.2.8, Hibernate version is 3.6.10
Create the sequence(CONF_ID_SEQ - if it doesn't exists) and make sure that you have "grant" permission for that.
Alternatively, you can create a "synonym" for the sequence and give "grant" permission for all the users or to the specific users you want to. Then it should work.
E.g
Consider, You are creating Sequence as "dbauser". Now, some other "user"(who don't have access) trying to use it,
then u will get "user has no privileges" error.
In that case you can use above strategy.
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 have two tables: foo (primary key: foo_id) and foo_entry (primary key: foo_entry_id; foreign key: foo_id).
Below is my Hibernate config.
My problem is, when I call getAttributes() on the FooModel class, I end up with a list of a little over one million null objects. (foo table has ~200 rows, foo_entry has ~10,000).
I'm new to Hibernate and suspect I am just overlooking or am just not understanding something very, very basic. Any help appreciated!
<hibernate-mapping package="com.blah.www">
<class name="FooModel" table="foo">
<id name="fooId" column="foo_id"></id>
<list name="attributes" table="foo_entry">
<key column="foo_id" />
<index column="entry_id" />
<one-to-many class="FooEntryModel" />
</list>
</class>
</hibernate-mapping>
<hibernate-mapping package="com.blah.www">
<class name="FooEntryModel" table="foo_entry">
<id name="fooEntryId" column="foo_entry_id">
<generator class="native" />
</id>
<property name="fooId" type="int" column="foo_id" />
<property name="attrName" type="string" column="attr_name" />
<property name="attrValue" type="string" column="attr_value" />
<property name="startDate" type="timestamp" column="start_date" />
<property name="endDate" type="timestamp" column="end_date" />
</class>
</hibernate-mapping>
The numbers imply you're getting a Cartesian join. Do you have the FK set up in the database?
aside - I used Hibernate for a a year and never coded an attribute-infused model or one of those files like your show. We always reverse-engineered the database.
First step to debug is to see the query, Hibernate generated for you, in the logs. However, I suggest you to try this,
<list name="attributes">
<key column="foo_id" />
<one-to-many class="FooEntryModel" />
</list>
Sigh...
This turned out to have a very logical (and very subtle) explanation.
I had misunderstood and hijacked the semantics of the <index> (also known as <list-index>) tag within <list>. Namely, given:
<list name="attributes">
<key column="foo_id" />
<index column="some_integer_value" />
<one-to-many class="FooEntryModel" />
</list>
... I thought was referring to the attribute by which you want to order the list. In fact, it refers to an attribute whose value denotes at what index position within the list to insert the overall object. It's meant to be a placeholder attribute, maintained and used entirely by Hibernate.
The value of the "some_integer_value" to which I was mapping varied in my test data. Sometimes the value was less than a 100. Sometimes it was greater than a million.
Thus, upon mapping just one row where "some_integer_value" == e.g. 100,001, Hibernate would create a list with that object inserted in the 100,001st position. Every list member preceding it, naturally, would be null.