I have the following setup where a class contains a collection. When querying instances of this class I like to populate a data transfer class rather than the data class. However, Hibernate generates a wrong SQL query. What am I missing?
The Hibernate mapping:
<class name="Thread" table="tbl_threads" schema="dbo">
<id name="Id" type="integer">
<column name="i_id"/>
<generator class="identity"/>
</id>
<set name="keywords" inverse="true" lazy="false" cascade="all-delete-orphan" optimistic-lock="false">
<key>
<column name="thread_id" not-null="true"/>
</key>
<one-to-many class="Comment"/>
</set>
<!-- ... -->
</class>
and
<class name="ThreadKeyword" table="tbl_keywords" schema="dbo">
<composite-id name="id"
class="se.ericsson.eab.sdk.fido.server.api.pojos.report.ReportThreadKeywordId">
<key-property name="keywordId" type="integer">
<column name="keyword_id" />
</key-property>
<key-property name="threadId" type="integer">
<column name="thread_id" />
</key-property>
</composite-id>
<!-- ... -->
</class>
The HQL I am using is
SELECT new Composite(t.id, t.keywords, ...)
FROM Thread t, ThreadKeyword tk
WHERE t.id = tk.id.threadId
This generates a SQL where the SELECT part contains only a dot for the keyword attribute:
select thread1_.report_id as col_0_0_, . as col_92_0_
from dbo.tbl_thread reportthre0_ inner join
dbo.tbl_keywords keywords4_ on reportthre0_.i_id=keywords4_.thread_id
It works fine when I query for the data class directly, i.e.
SELECT t
FROM Thread t, ThreadKeyword tk
WHERE t.id = tk.id.threadId
As I understand will Hibernate not find a column name for keywords in the thread table. That is right, since it is a collection. It rather needs to be populated using subsequent queries. If I omit the keywords in the constructor for the Composite class the query gets right but Hibernate won't populate the Set.
How do I get the keywords set populated?
You cannot do that with a collection.
t.id is a column/value
so Hibernate translates that into thread1_.report_id as col_0_0_. Hibernate even gave it the alias col_0_0_
t.keywords is a set of values so Hibernate just can't translate the collection into a column/value.
A query includes a list of columns to be included in the final result
immediately following the SELECT keyword - Wikipedia
Now the
SELECT t FROM Thread t, ThreadKeyword tk WHERE t.id = tk.id.threadId
works fine because Hibernate knows how to translate the query you have there into SQL.
Related
I am taking an existing Java application and working on updating it from Hibernate 3 where we used hbm.xml files for Entity Mappings. We are now using Hibernate 5.5.5.Final and the code compiles with ehcache, but now I get an error with the code when starting to run it.
I should start off that one of the Hibernate properties is:
validate
The error message I am getting now is:
org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [my_db_dev.Project_myTemplateInfos]
at org.hibernate.tool.schema.internal.AbstractSchemaValidator.validateTable(AbstractSchemaValidator.java:121)
at org.hibernate.tool.schema.internal.GroupedSchemaValidatorImpl.validateTables(GroupedSchemaValidatorImpl.java:42)
at org.hibernate.tool.schema.internal.AbstractSchemaValidator.performValidation(AbstractSchemaValidator.java:89)
at org.hibernate.tool.schema.internal.AbstractSchemaValidator.doValidation(AbstractSchemaValidator.java:68)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:200)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:81)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:327)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:471)
I would love to completely remove all the hbm.xml files and replace with them with Entity Mapping POJO's with annotations, however, that is not an option right now. The existing application has this different object model that goes throughout, so I don't want to mess with that right now. That will be in the next phase.
According to the error I am missing a table named 'Project_myTemplateInfos' and there is no table with this name. Instead, there is a table named 'Project' and the hbm.xml file for this is as follows.
<hibernate-mapping package="com.myApp.server.model">
<class name="Project" table="project" dynamic-update="true">
<id name="id" column="id">
<generator class="native" />
</id>
<property name="name" not-null="true"/>
<property name="displayCity" not-null="true"/>
<list name="myTemplateInfos" cascade="all, delete-orphan" lazy="false" >
<key column="projectId" not-null="false" />
<list-index column="listIndex" />
<composite-element class="com.myApp.server.model.MyTemplateInfo" >
<property name="name" not-null="false" />
<property name="frequency" not-null="false" />
</composite-element>
</list>
</class>
</hibernate-mapping>
As you can see 'myTemplateInfos' is a List within the Project table. After the POJO is created, it looks like something like this.
#ModelBean(IProject.class)
#PermissionIdentifier("project")
public class Project extends ModelObject implements Serializable, IProject {
private Long id;
private String displayCity = "";
private List<IMyTemplateInfo> myTemplateInfos = Lists.newArrayList();
// getters and setters
// hashcode and equals
}
Next we do have another table in the database that is called 'myTemplateInfos' and we do have an hbm xml file for that table as follows ... actually we do not have an hbm xml file for this, so maybe that is the issue. I am going to create a hbm xml file for this and see if that solves the problem.
We do have a POJO for this object 'MyTemplateInfo' though.
If I simply remove his List from the hbm mapping and the Project object, the problem goes away of course, but there is another Set in the hbm.xml file which would give me the same problem, but with a new missing table.
The question becomes how to fix this error message. Is the problem within the hbm xml file for 'Project', or is it in the Project POJO, or the fact that an hbm file does not exist for the 'MyTemplateInfo'?
The solution to this was to fix the hbm xml mapping. Since I haven't had to do this in over 15 years, I am very rusty with it. I can't tell you how happy I was back then to switch to Java POJO's for Hibernate Entity classes with Annotations. But now, unfortunately, I am back having to deal with these xml files again.
The table I had 'mycommunitytemplateinfos' I created a new hbm xml file for it as follows, and put this in the hibernate.cfg.xml file before the other hbm xml mapping.
<hibernate-mapping package="com.myapp.server.model">
<class name="MyTemplateInfo" table="mytemplateinfos">
<id name="id" column="projectId">
<generator class="native"/>
</id>
<property name="name" not-null="false" />
<property name="frequency" not-null="false" />
</class>
</hibernate-mapping>
The mapping between this and the actual class is fine as I have tested this out. I put the hbm file in the hibernate.cfg.xml file before the Project.hbm.xml file and modified the Project.hbm.xml with a one-to-many tag as follows:
<list name="myTemplateInfos" cascade="all, delete-orphan" lazy="false" >
<key column="projectId" not-null="false" />
<list-index column="listIndex" />
<one-to-many class="com.myApp.server.model.MyTemplateInfo" />
</list>
And this seemed to work. I had to do something like this a few times until I got the mapping right. In this day of age, there isn't a lot of information about hbm xml files. Hibernate 5 does use these, but I understand that the preferred way is annotated Java POJO's. Unfortunately, I am stuck in a situation where I can't do that yet.
I'm stuck for a moment now and I need some help.
I wanna have a mapping in hibernate between 2 fields that are not PK in any of the both Tables
Table Category (
catId Numeric(10),
categoryBusinessRef Numeric(10)
)
Table Product (
productId Numeric(10),
categoryBusinessRef Numeric(10)
)
The Sql query would be :
SELECT *
from Category as a
Join Product as b on a.categoryBusinessRef = b.categoryBusinessRef
But in Hibernate it make me this mapping
SELECT *
from Category as a
Join Product as b on a.categoryId = b.categoryBusinessRef
My hbms looks likes
<class name="Category" table="A">
<id name="categoryId" length="10">
<column name="categoryid" />
<generator class="sequence">
<param name="sequence">S_category</param>
</generator>
</id>
<set name="categoryBusinessRefs" table="B" >
<key>
<column name="categoryBusinessRef" />
</key>
<one-to-many class="ProductClass" />
</set>
</class>
<class name="Product" table="B">
<id name="productId" length="10">
<column name="productid" />
<generator class="sequence">
<param name="sequence">S_product</param>
</generator>
</id>
<property name="categoryBusinessRef" length="10">
<column name="categoryBusinessRef" />
</property>
</class>
So it's a One-to-many relation but It have to map with another values as the PK of Category
Thanks for the help
EDIT :
If I can't do that ok ! But If I can I want to do that, I know all the stuff with the primary key but my question is not "Mapping in Hibernate on PK" but "Mapping in Hibernate on another field than the PK", so the answers who said map to the PK doesn't interest me :p
Your model is wrong, unless you want to have a cartesian product (it is a rare requirement) you should always join by the primary key.
You can have one-to-one, one-to-many or many-to-many relationships (the last two are more common).
In one-to-many you need the pk of the one side in the many side.
In many-to-may you need a relationship table that makes the mapping.
Assuming that you want you want that many B's connect to the same A (so one-to-many).
CREATE TABLE a (
id NUMERIC(10) PRIMARY KEY,
disc NUMERIC(10) NOT NULL
)
CREATE TABLE b (
id NUMERIC(10) PRIMARY KEY,
a_id NUMERIC(10) NOT NUL REFERENCES a (id)
)
Now you can query
SELECT *
FROM a
JOIN b ON b.a_id = a.id
and recover all discs from a with the proper relation to all b.
I'm very new to hibernate, and I'm trying to set up a new method in our PersonDAO.
My hibernate mapping file looks like this:
<?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 package="com.foo.bar.domain">
<class name="Person" table="person">
<meta attribute="class-description">A Person</meta>
<id name="id" type="java.lang.Long" column="rid" unsaved-value="null">
<generator class="native" />
</id>
<version name="version" type="integer" column="rversion" unsaved-value="null" />
<property name="UID" type="string" column="UID" length="16" not-null="true" unique="true"/>
<property name="lastName" type="string" column="last_name" not-null="true" />
<property name="firstName" type="string" column="first_name" not-null="true" />
<property name="ownDepartment" type="string" column="own_department"/>
<!-- a person has many responsibilities and a responsibility can can assigned to many person -->
<set name="responsibilities" table="person_responsibility">
<key column="person_id"/>
<many-to-many column="responsibility_id" class="Responsibility"/>
</set>
<set name="additionalDepartments" table="PERSON_TO_ADDL_DEPARTMENT">
<key column="person_id"/>
<element column="ADDITIONAL_DEPARTMENT" type="string"/>
</set>
</class>
and I've written a method like this, in java, to fetch all the managers from a given department:
public List<Person> getManagerByDepartment(final String givenDepartment){
List<Person> l = (List<Person>) this.getHibernateTemplate().executeFind(new HibernateCallback<List<Person>>() {
public List<Person> doInHibernate(Session session) throws HibernateException, SQLException {
String query = "select p from Person p join p.responsibilities responsibilities join p.additionalDepartments additionalDepartments where responsibilities.name = 'manager' and (p.ownDepartment = :givenDepartment or additionalDepartments = :givenDepartment)";
List<Person> result = (List<Person>) session.createQuery(query).setString("givenDepartment", givenDepartment).list();
return result;
}
});
return l;
}
now I do a manual query in SQL, and I can see that for a given department, there are definitely more than one people who have the additional responsibility 'manager'...why does my method only ever return one person, instead of all of them?
I have a strong suspicion that my method, specifically my query, and not the mapping, is the issue, but I can't see what's wrong with it...
I've jumped in at the deep end here, so any help would be very much appreciated.
edit: note, I'm working on hundreds of records, not millions, and this isn't exactly a bottle-neck operation, so I'm not too worried about performance...that said if I'm doing something that's pointlessly wasteful, do point it out
You can print hibernate query by enabling showsql option and check the query getting created and then test it against the database.
Hard to be sure without the sample data, but when you do join in HQL, it is translated to inner join in SQL. So, if you know that there should be more than one result with given responsibility then the problem is probably join p.additionalDepartments.
Try this query with left join for additionalDepartments and see if it works
String query = "select p from Person p join p.responsibilities responsibilities left join p.additionalDepartments additionalDepartments where responsibilities.name = 'manager' and (p.ownDepartment = :givenDepartment or additionalDepartments = :givenDepartment)";
I have done some maintenances in a web system, this system was done in Java, Struts2 and Hibernate 3. One of the items that my customer asked me urgently was to correct the Login, because it's very slow.
I could identify when the user does the login, the Hibernate generates many queries on the database in different tables which does not have relation with login.
My relationship are three tables: User, Student, Teacher
Student
ID
Name
Teacher
ID
Name
User
ID
Login
Password
Every time that a student does a login the query check if it's the correct Login and Password and if the ID exists in a Student table. As you can see there is no foreign key ID, as for example, ID_USER in a Student table.
My Query:
Student student =(Student)session.createCriteria(Student.class)
.add( Property.forName("login").eq(login) )
.add( Property.forName("password").eq(password) )
.setMaxResults(1)
.uniqueResult();
Student inherits from User...
My User.hbm.xml
...
<class name="com.xxx.User" table="TBL_USERS" discriminator-value="0" lazy="true">
<id name="id" column="ID">
<generator class="sequence">
<param name="sequence">seq_users</param>
</generator>
</id>
<discriminator column="TYPE" insert="true" />
<subclass name="com.xxx.Student" discriminator-value="1" lazy="true">
<join table="TBL_STUDENTS">
<key column="ID"/>
<property name="name" column="NAME"/>
</join>
</subclass>
<subclass name="com.xxx.Teacher" discriminator-value="2" lazy="true">
<join table="TBL_TEACHERS">
<key column="ID" />
<property name="name" column="NAME" />
</join>
</subclass>
...
In my Hibernate log many strange queries, like bellow:
It's happening in more than 20 different tables and repeating all the times, because that, the login action is slow. But I don't know why it's happening, I don't have much experience with Hibernate.
Hibernate:
select
exerc0_.ID_CLASS as ID6_1_,
exerc0_.ID as ID1_,
exerc0_.ID as ID14_0_,
exerc0_.DATE_ENT as DATA2_14_0_,
exerc0_.TITLE as TITTLE14_0_,
exerc0_.TEXT as TEXT14_0_,
exerc0_.TYPE_EXERC as TYPE5_14_0_,
exerc0_.ID_CLASS as ID6_14_0_
from
TBL_EXERC exerc0_
where
exerc0_.ID_CLASS=?
What could be happening?
Just to not leave this question open forever.
The problem was fixed many time ago, but I forgot completely to reply.
I had to enable few lazy-loadings and make sure to choose LIST instead SET in few other places. Seemed more performatic with LIST.
#Firo was right in somehow.
I have a user and a message table. User to Message are one-to-many relationships and Message to User are many-to-one relationships. I've marked one of the many-to-one as fetch join. When I 'get' a single Message, Hibernate runs a join query, but when I fetch all the messages, Hibernate runs select queries instead of a join. What could be the reason? Following are the details:
Relationship between them:
User
<set name="messagesForFromUserUid" lazy="true" table="message" inverse="true" cascade="save-update">
<key>
<column name="from_user_uid" not-null="true" />
</key>
<one-to-many class="repository.Message" />
</set>
<set name="messagesForToUserUid" lazy="true" table="message" fetch="select">
<key>
<column name="to_user_uid" not-null="true" />
</key>
<one-to-many class="repository.Message" />
</set>
Message
<many-to-one name="userByFromUserUid" class="repository.User" fetch="join" lazy="false">
<column name="from_user_uid" not-null="true" />
</many-to-one>
<many-to-one name="userByToUserUid" class="repository.User" fetch="select" lazy="proxy">
<column name="to_user_uid" not-null="true" />
</many-to-one>
When I fetch a single message object, Hibernate runs one join query as expected:
Message m = (Message) s.get(Message.class, 2);
Hibernate:
select
message0_.message_uid as message1_1_1_,
message0_.from_user_uid as from2_1_1_,
message0_.to_user_uid as to3_1_1_,
message0_.message_text as message4_1_1_,
message0_.created_dt as created5_1_1_,
user1_.user_uid as user1_0_0_,
user1_.user_name as user2_0_0_,
user1_.user_password as user3_0_0_,
user1_.email as email0_0_,
user1_.first_name as first5_0_0_,
user1_.last_name as last6_0_0_,
user1_.created_dt as created7_0_0_
from
hello.message message0_
inner join
hello.user user1_
on message0_.from_user_uid=user1_.user_uid
where
message0_.message_uid=?
But when I fetch all the messages in one go, Hibernate runs select queries instead:
List<Message> l = s.createQuery("from Message").list();
Hibernate:
select
message0_.message_uid as message1_1_,
message0_.from_user_uid as from2_1_,
message0_.to_user_uid as to3_1_,
message0_.message_text as message4_1_,
message0_.created_dt as created5_1_
from
hello.message message0_
Hibernate:
select
user0_.user_uid as user1_0_0_,
user0_.user_name as user2_0_0_,
user0_.user_password as user3_0_0_,
user0_.email as email0_0_,
user0_.first_name as first5_0_0_,
user0_.last_name as last6_0_0_,
user0_.created_dt as created7_0_0_
from
hello.user user0_
where
user0_.user_uid=?
Hibernate:
select
user0_.user_uid as user1_0_0_,
user0_.user_name as user2_0_0_,
user0_.user_password as user3_0_0_,
user0_.email as email0_0_,
user0_.first_name as first5_0_0_,
user0_.last_name as last6_0_0_,
user0_.created_dt as created7_0_0_
from
hello.user user0_
where
user0_.user_uid=?
Looks like Hibernate does not always use fetch strategies defined in the mapping, for HQL or Criteria queries. They are typically used for get/load. Found a reference here: https://forum.hibernate.org/viewtopic.php?f=1&t=957561
I am not sure but this might be the reason. If you run single query which is join or subquery. It is ok, there may not be any performance difference for single query. But if you are running multiple queries(in your case multiple messages), There may raise Performance issue. I would definitely choose simple select queries instead of join/subqueries. This definitely makes sense if we consider performance. This is what Hibernate does.