In our code have something along the lines of these two Java classes:
public class MailMessage {
private String id;
private String message;
private String sentTime;
// getters and setters
}
public class MailMessageWithRecipients extends MailMessage {
private String recipients;
// getter and setter for recipients
}
The purpose of separating the recipients list is that, since it can be very large, we would like to avoid loading it when possible. We would like to be able to map both MailMessage and MailMessageWithRecipients to a single table; mail_messages. However, if I try to do this in our Hibernate mapping file -
<class name="MailMessage" table="mail_messages" dynamic-insert="true" dynamic-update="true">
<id name="id" column="message_id" length="32">
<generator class="uuid" />
</id>
<property name="message" column="message" />
<property name="sentTime" column="sentTime" />
</class>
<class name="MailMessageWithRecipients" table="mail_messages" dynamic-insert="true" dynamic-update="true">
<id name="id" column="message_id" length="32">
<generator class="uuid" />
</id>
<property name="message" column="message" />
<property name="sentTime" column="sentTime" />
<property name="recipients">
<column name="recipients" sql-type="longblob" />
</property>
</class>
And then write a query something like this:
Query query = session.createQuery("from MailMessage where id = :id");
query.setParameter("id", id);
MailMessage message = query.uniqueResult();
I get a "org.hibernate.NonUniqueResultException: query did not return a unique result" exception when the query is executed, even when there is only a single row in the mail_messages table. I suspect this is because it sees that row as being both a MailMessage and a MailMessageWithRecipients. Is there a way to map these two objects to the same table this way while maintaining the parent-child Java relationship? Related discussions I've seen recommend a component relationship in the mapping file, but it seems to me that that would involve removing the inheritance structure and turning MailMessageWithRecipients into a facade structure for MailMessage. If that's the only way this can be done, I'll do it, but I was wondering if there was a mapping that allows the inheritance structure to remain intact, as that would involve significantly less refactoring of our code.
Related
I have two entities, Project and ProjectFee:
public class Project implements Serializable {
private Integer id;
//some more fields
private Set<ProjectFees> fees = new LinkedHashSet<>();
}
public class ProjectFee implements Serializable {
//some more fields
private Integer idProject; //id of the Project ProjectFee is related to
}
Mapped as:
<class name="com.package.Project" table="project">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<!-- more fields -->
<set name="fees" table="project_fees" cascade="all" fetch="join" lazy="false">
<key>
<column name="id_project"/>
</key>
<one-to-many class="com.package.ProjectFees"/>
</set>
</class>
<class name="com.package.ProjectFees" table="project_fees">
<!-- more fields -->
<property name="idProject" type="java.lang.Integer">
<column name="id_project" />
</property>
</class>
So when I try to create a new Project and a new ProjectFee using the cascade I get an error because idProject cannot be null in db and it is not setted since Project is still not created so it hasn't id. I know I can solve it by doing, on create method:
Remove ProjectFees from Project
Create Project
Add ProjectFees to Project setting idProject for each one
Update Project
But this is kind of a "dirty" way so I would like to know if there is some way to do this automatically
Change your ProjectFees mapping to this:
<many-to-one name="idProject" class="com.gesmifid2.common.model.Project">
<column name="id_project" />
</many-to-one>
Edit:
Your ProjectFee class should look like this:
public class ProjectFee implements Serializable {
//some more fields
private Project project; //project ProjectFee is related to
}
And every time you add new ProjectFee to the Project, you should set that Project in ProjectFee. Example of method in Project class:
public void addProjectFee( ProjectFee pf){
fees.add(pf);
pf.setProject(this);
}
If you closely look your ProjectFee class, there is no way for it to know that this is somehow related to class Project (as a foreign key id_project). That is why when you save both objects together, ProjectFee class dont know that it should populate the column id_project with id column value of Project class.
There are two possibilities -
Either you populate the id_project column by yourself (as you mentioned in question) by saving Project object first and then providing ID to the ProjectFee object.
Or, make a foreign key relation in you entities by making id_project property of type Project and join both entities on column id/id_project.
Hope that helps.
I don't know how to exactly express what I'm looking for, so I'll explain what I have and then what I'm trying to do.
I have a Class model, which has 2 classes with a One-To-Many unidirectional relationship.
public class TaxType extends Entity implements java.io.Serializable {
//stuff
private Set<TaxTypeAttribute> listTaxTypeAttribute = new HashSet<>(0);
}
public class TaxTypeAttribute extends Entity implements java.io.Serializable {
private String attributeName;
//stuff, but no reference to TaxType
}
Entity Class is like a primary key standard, we call it "OID design pattern", but no clue if it's like that in english.
public class Entity {
private String oid;
//constructor, get and set
}
On the mapping, it goes like this:
<class name="entity.TaxType" table="taxttype" catalog="tax_type" optimistic-lock="version">
<id name="oid" type="string">
<column name="OIDtt" length="50" />
<generator class="uuid2" />
</id>
<set name="listAtributoTipoImpuesto">
<key column="OIDtt" not-null="true"/>
<one-to-many class="entidades.AtributoTipoImpuesto" />
</set>
</class>
<!-- two separated files, this is just for showing -->
<class name="entity.TaxTypeAttribute" table="taxtypeattribute" catalog="tax_type" optimistic-lock="version">
<id name="oid" type="string">
<column name="OIDtta" length="50" />
<generator class="uuid2" />
</id>
<property name="attributeName" type="string">
<column name="attributeName" length="50" not-null="true" />
</property>
</class>
In one step of the program, I have a TaxType and the attributeName from a TaxTypeAttribute, but I need to get the full TaxTypeAttribute. I'm making querys through Criteria API. I could do taxType.getListTaxTypeAttribute(); and do a loop until I find the object, but I would like to know if there's a way to do it using some Hibernate querys.
I've tried doing taxType.getOid(); and then using that and the attributeName but it throws an exception:
Exception in thread "main" org.hibernate.QueryException: could not resolve property: OIDtt of: entity.TaxTypeAttribute
Any clues? Thanks you, and excuse me for my bad english
EDIT: In order to follow design patterns, we use this method to do SELECT querys: Awful thing we use for querys.
The way I did it is this:
ArrayList<DTOCriteria> criteriaList = new ArrayList<>();
DTOCriteria c1 = new DTOCriteria();
c1.setAttribute("OIDtt");
c1.setOperation("=");
c1.setValue(taxType.getOID());
criteriaList.add(c1);
ArrayList<Object> found = search("TaxTypeAttribute");
I could add another DTOCriteria if I want ("attributeName";"=";attributeName, for example), but if the former doesn't works it's kind of useless. I also tried (just because it's free) using "TaxType" as attribute and the TaxType object as a value, but didn't work either.
PS: the code works. I use it for other querys and works, it just doesn't work for this one, or I don't know how to make it work. May be you can't do that kind of search, I don't know.
From an HQL/JPQL perspective, you could write your query as:
SELECT tta FROM TaxType tt JOIN tt.listTaxTypeAttribute tta
WHERE tt.oid = :oid
AND tta.attributeName = :attributeName
This query will return you TaxTypeAttribute instances that match the specified criteria. How you translate that into your query language is something which I cannot aid with.
I am stuck with this Hibernate thing and dont know how to figure it out. Please help !
So I have these two tables:
POSITION
positionid(PK), description
JOB
jobid(PK),positionid(FK),description
How do I use HQL in Hibernate to fetch all jobs with their corresponding position descriptions ?
Edit: So this what I am trying to achieve:
JOBID POSITION.DESCRPTION JOB.DESCRIPTION
1 Teacher Science Teacher
2 Coach Football Coach
and so on for all job's in JOB table. I am trying to figure out what will be HQL for this.
I have put together the following code till now :
position.hbm.xml
<hibernate-mapping>
<class name="com.XXXX.model.Position" table="POSITION">
<id name="positionID" type="int" column="POSITIONID" >
<generator class="assigned"/>
</id>
<property name="description">
<column name="DESCRIPTION" />
</property>
<set name="jobs">
<key column="positionID" />
<one-to-many class="com.XXXX.model.Job" />
</set>
</class>
</hibernate-mapping>
job.hbm.xml
<hibernate-mapping>
<class name="com.XXXX.model.Job" table="JOB">
<id name="jobID" type="int" column="JOBID" >
<generator class="assigned"/>
</id>
<property name="description">
<column name="DESCRIPTION" />
</property>
<many-to-one name="position" class="com.XXXX.model.Position" column="positionID" />
</class>
</hibernate-mapping>
Position.java
public class Position {
private int positionID;
private String description;
private Set<Job> jobs = new HashSet<Job>();
// Getters and Setters for all the above three follows...
}
Job.java
public class Job {
private int jobID;
private String description;
private Position position;
// Getters and Setters for all the above three follows...
}
In my code now I use
session.createQuery("from Position as p left join p.positionID as pid").list();
I know its not exactly correct and I am getting the follow error:
java.lang.NullPointerException
at org.hibernate.hql.ast.HqlSqlWalker.createFromJoinElement(HqlSqlWalker.java:317)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.joinElement(HqlSqlBaseWalker.java:3268)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromElement(HqlSqlBaseWalker.java:3060)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromElementList(HqlSqlBaseWalker.java:2938)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromClause(HqlSqlBaseWalker.java:688)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:544)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:281)
.........................
...........................
Can someone tell me how to fix this please ?
If you only want these three columns, then the HQL should be
select job.id, position.decription, job.description
from Job job
left join job.position position
This query will return a List<Object[]> and each Object array in the list will contain these three elements.
It would be much more natural however to use this query:
select job from Job job
left join fetch job.position
which would load all the jobs with their position. The query would return a List, and you would be able to access the three information uou want using job.getId(), job.getPosition().getDescription(), and job.getDescription().
The syntax of HQL is described with examples in the reference documentation, a must-read.
I think you can just getJobs() from the Position object
I have the following domain objects
Loan {
int id;
Date attribute1;
}
LoanExtension {
Date attribute2;
}
I would like to keep my objects like this because sometimes I would like to change only the attributes in LoanExtension in my database (i.e. attribute1 in the loan object will be null in the object and i don't want this to get set in the database).
How is this possible using a hibernate mapping with xml? I have done the following
<class name="org.domain.borrowerReview.Loan" table="loan_profiles" >
<cache usage="read-only"/>
<id name="loanId" column="id">
<generator class="native"/>
</id>
<version name="attribute1" column="date_1/>
<subclass name="org.domain.borrowerReview.LoanExtension" extends="org.rangde.domain.borrowerReview.LoanProfilesUpdate">
<property name="attribute2" column="date_2" />
</subclass>
</class>
I'm getting this exception :
Discriminator is needed when 'single-table-per-hierarchy' is used and a class has subclasses
Short answer
You need to add a discriminator column and change the snippet to something like this.
<class name="org.domain.borrowerReview.Loan" table="loan_profiles" >
<cache usage="read-only"/>
<id name="loanId" column="id">
<generator class="native"/>
</id>
<version name="attribute1" column="date_1/>
<discriminator column="loan_profiles_type" type="string"/>
<subclass name="org.domain.borrowerReview.LoanExtension" extends="org.rangde.domain.borrowerReview.LoanProfilesUpdate">
<property name="attribute2" column="date_2" />
</subclass>
</class>
Long answer
First off, do remember that the inheritance in JPA is not an absolute parallel to regular inheritance.
One must evaluate how the design of classes will have an impact on the underlying schema.
You have not mentioned how you would like to have your table structure as.
Hibernate provides three mechanisms for inheritance.
table per class hierarchy
table per subclass
table per concrete class
See detaiils here
Your xml snippet suggests that you are using table per class hierarchy.
Now, if one has subclasses, there would be little use if the is just one subclass (though it is allowed). And under "table per class hierarchy" all the sub classes are placed in the same table.
To distinguish between which subclass a particular record belongs to, hibernate relies on discriminator column.
You need to define the same.
Refer the following documentation documentation.
The example at the end of hibernate section 5.1.3 does not show an example on passing parameters.
There is no difference between a view
and a base table for a Hibernate
mapping. This is transparent at the
database level, although some DBMS do
not support views properly, especially
with updates. Sometimes you want to
use a view, but you cannot create one
in the database (i.e. with a legacy
schema). In this case, you can map an
immutable and read-only entity to a
given SQL subselect expression:
<class name="Summary">
<subselect>
select item.name, max(bid.amount), count(*)
from item
join bid on bid.item_id = item.id
group by item.name
</subselect>
<synchronize table="item"/>
<synchronize table="bid"/>
<id name="name"/>
...
</class>
Is it possible? And if so, how?
Thanks,
Franz
I don't think that it is possible, because the mapping file is like a static description.
Since Hibernate 3 you can use formulas to map this types of readonly calculated fields. Example:
#Formula("(SELECT b.BANK_NAME FROM " +
" BANK_INFORMATION b, BILLING_AGENT_BANK ba " +
" WHERE ba.CNPJ = COMPANY_CNPJ " +
" AND b.BANK_ID = ba.BANK_ID)")
public String getBankName() {
return bankName;
}
This example is with a Annotated property, but you can do the same in the mapping file.
In NHibernate:
<class name="Blog" mutable="false">
<subselect>
SELECT Blog.Id, Blog.Author, Blog.Title, Comment.Comment
FROM Blog INNER JOIN Comment ON Blog.Id = Comment.Blog_id
WHERE Comment.LanguageId = :blogcomment.languageId
</subselect>
<id name="Id">
<generator class="assigned" />
</id>
<property name="Author" />
<property name="Title" />
<property name="Comment" />