I have three tables device , vehicle and vehicle_device all tables are in one schema 'tcm' and I'm trying to get 'vehicle' by 'device imei' . In Vehicle.class i have field 'Set devices' and create maping:
Vehicle.hbm.xml
<hibernate-mapping package="hibernate.entity">
<class name="Vehicle" table="vehicles" schema="tcm">
<id name="id" type="integer" column="id">
<generator class="sequence">
<param name="sequence">tcm.vehicles_id_seq</param>
</generator>
</id>
.
.
.
<set name="devices" table="vehicle_device"
inverse="false" lazy="true" fetch="select" cascade="all" >
<key>
<column name="vehicle_id" not-null="true" />
</key>
<many-to-many entity-name="hibernate.entity.Device">
<column name="device_id" not-null="true" />
</many-to-many>
</set>
</class>
when execute criteria to take the results for needed 'device imei'
#Override
public Vehicle getVehicleByDeviceImei(String imei) {
Criteria criteria = getSession().createCriteria(Vehicle.class);
criteria.createAlias("devices", "devices").add(Restrictions.eq("devices.imei", imei));
Vehicle v = (Vehicle) criteria.uniqueResult();
return v;
}
Everything is OK except that the query that is generated does not add schema 'tcm' for the link table 'vehicle_device' in first inner join
select .
.
.
.
from
tcm.vehicles this_
inner join
vehicle_device devices3_
on this_.id=devices3_.vehicle_id
inner join
devices devices1_
on devices3_.device_id=devices1_.id
where
devices1_.device_imei=?
and have an error :
ERROR: relation "vehicle_device" does not exist
LINE 17: vehicle_device devices3_
If i add manual schema to generated query --> 'tcm.vehicle_device' it work.
how to fix my configuration so Hibernate Criteria adds the schema for table 'vehicle_device'.
I fix problem. I just added scheme = 'tcm' in set tag in mapping
Related
Consider the following hibernate mapping (using hibernate 4):
Answer with a DataCollection joined-subclass:
<hibernate-mapping>
<class name="Answer" table="answer">
<many-to-one name="answeredForm" class="AnsweredForm" fetch="select">
<column name="answered_form_id" />
</many-to-one>
<joined-subclass table="data_collection" name="DataCollection" extends="Answer">
<key column="id"></key>
</joined-subclass>
</class>
</hibernate-mapping>
AnsweredForm with a PatientForm joined-subclass:
<hibernate-mapping>
<class name="AnsweredForm" table="answered_form">
<joined-subclass table="patientForm" name="PatientForm" extends="AnsweredForm">
<many-to-one name="patient" class="Patient" fetch="join">
<column name="patient_id" />
</many-to-one>
</joined-subclass>
</class>
</hibernate-mapping>
Question: Using HQL, how can you ask for "all datcollections whose AnsweredForm belongs to patient x"?
SELECT answer FROM DataCollection answer
JOIN answeredForm answeredForm
WHERE answer.answeredForm.patient.code=:patientCode
This HQL yields the error:
ERROR: missing FROM-clause entry for table "answeredfo2_2_"
And rightly so, as the SQL translation of this query is:
SELECT datacollec0_.id AS id1_62_,
datacollec0_1_.free_text AS free_tex2_62_
FROM data_collection datacollec0_
INNER JOIN answer datacollec0_1_
ON datacollec0_.id = datacollec0_1_.id
CROSS JOIN answered_form answeredfo1_
CROSS JOIN answered_form answeredfo2_
CROSS JOIN patient patient3_
WHERE datacollec0_1_.answered_form_id = answeredfo1_.id
AND datacollec0_1_.answered_form_id = answeredfo2_.id
AND answeredfo2_2_.patient_id = patient3_.id
AND CASE
WHEN answeredfo1_4_.id IS NOT NULL THEN 4
WHEN answeredfo1_1_.id IS NOT NULL THEN 1
WHEN answeredfo1_2_.id IS NOT NULL THEN 2
WHEN answeredfo1_3_.id IS NOT NULL THEN 3
WHEN answeredfo1_.id IS NOT NULL THEN 0
END = 2
AND patient3_.code = ?
Try to use the following hql:
SELECT a FROM Answer a
JOIN a.answeredForm af
WHERE af.patient.code=:patientCode
Look at this part of the hibernate documentation for additional explanations and examples.
When I put inverse=true into set, nothing gets deleted. When I don't, and I remove MealIngredient from set, then Hibernate tries to set null, it fails and exception is thrown:
[SQLITE_CONSTRAINT] Abort due to constraint violation (MealIngredients.mealId may not be NULL)
Here are XML mappings:
<class name="restaurant.meal.Meal" table="Meals">
<id name="id" type="integer">
<column name="id" not-null="true" unique="true"/>
<generator class="increment"/>
</id>
<!-- some other, simple properties -->
<set name="ingredientsSet" cascade="all" lazy="false">
<key>
<column name="mealId" not-null="true" />
</key>
<one-to-many class="restaurant.meal.MealIngredient" />
</set>
</class>
<class name="restaurant.meal.MealIngredient" table="MealIngredients">
<composite-id name="id" class="restaurant.meal.MealIngredient$Id">
<key-property name="ingredientId" />
<key-property name="mealId" />
</composite-id>
<many-to-one name="ingredient" class="restaurant.storage.Ingredient" insert="false" update="false" lazy="false">
<column name="ingredientId" not-null="true" />
</many-to-one>
<many-to-one name="meal" class="restaurant.meal.Meal" insert="false" update="false" lazy="false">
<column name="mealId" not-null="true" />
</many-to-one>
<!-- other properties -->
</class>
Yes, the relationship between Meal and Ingredient is many-to-many with join table MealIngredient (and yes, I have to map MealIngredient as well, because of additional columns in that table).
This question did not help me, neither did this.
Edit:
Only inserting works with current mapping, update just generates another row in MealIngredient table.
Edit 2: hashCode and equals implementations:
MealIngredient$Id: (uses Apache commons-lang EqualsBuilder and HashCodeBuilder)
#Override
public boolean equals(Object o) {
if(!(o instanceof Id))
return false;
Id other = (Id) o;
return new EqualsBuilder()
.append(this.getMealId(), other.getMealId())
.append(this.getIngredientId(), other.getIngredientId())
.isEquals();
}
#Override
public int hashCode() {
return new HashCodeBuilder()
.append(this.getMealId())
.append(this.getIngredientId())
.hashCode();
}
MealIngredient:
#Override
public boolean equals(Object o)
{
if(!(o instanceof MealIngredient))
return false;
MealIngredient other = (MealIngredient) o;
return this.getId().equals(other.getId());
}
#Override
public int hashCode()
{
return this.getId().hashCode();
}
I checked log and although I don't know what Hibernate does under the hood, but it does make the insert into MealIngredient:
15:42:53,122 TRACE IntegerType:172 - returning '5' as column: quantity3_
Hibernate:
insert
into
MealIngredients
(quantity, ingredientId, mealId)
values
(?, ?, ?)
15:42:53,131 TRACE IntegerType:133 - binding '16' to parameter: 1
15:42:53,131 TRACE IntegerType:133 - binding '5' to parameter: 2
15:42:53,131 TRACE IntegerType:133 - binding '1' to parameter: 3
And when Iāremove MealIngredient from Meal.ingredientsSet, Hibernate makes update and tries to set mealId to null:
Hibernate:
update
MealIngredients
set
quantity=?
where
ingredientId=?
and mealId=?
15:48:57,529 TRACE IntegerType:126 - binding null to parameter: 1
15:48:57,529 TRACE IntegerType:133 - binding '1' to parameter: 2
15:48:57,531 TRACE IntegerType:133 - binding '1' to parameter: 3
15:48:57,535 WARN JDBCExceptionReporter:77 - SQL Error: 0, SQLState: null
15:48:57,535 ERROR JDBCExceptionReporter:78 - [SQLITE_CONSTRAINT] Abort due to constraint violation (MealIngredients.quantity may not be NULL)
I believe the explanation you're looking for is here. Well, sort of. Don't read his explanation, it confuses me. His examples are excellent though.
So, anyways, I think you want to do one of the following:
inverse=false and remove the mealIngredient from your ingredients
collection and then save the Meal
inverse=true and have to null the meal instance variable in MealIngredient and save the MealIngredient
EDIT: The issue with inserts instead of updates is probably due to the fact that you have not over-ridden hashcode and equals. If you're using Eclipse, I believe it can do it for you, but you must tell it to use both properties of your composite key when it auto generates the methods. Per Hibernate documentation chapter 5:
The persistent class must override equals() and hashCode() to
implement composite identifier equality. It must also implement
Serializable.
Unfortunately, it seems that Hibernate does not work well with composite primary keys. I had to add extra ID column into many-to-many join tables (like my MealIngredient) and work with that.
After I use extra ID as primary key, inserting/updating/deleting works as expected (even with cascade set to delete-orphan, cascade deleting works!).
I provide final mappings for entities Meal and MealIngredient, for future reference. I hope this will help others, when they stumble upon many-to-many relationships with additional properties/columns in join table.
<class name="restaurant.meal.Meal" table="Meals">
<id name="id" type="integer">
<column name="id" not-null="true" unique="true"/>
<generator class="increment"/>
</id>
<!-- additional properties -->
<set name="ingredientsSet" table="MealIngredients" cascade="all-delete-orphan" lazy="false" inverse="true">
<key update="true">
<column name="mealId" not-null="true" />
</key>
<one-to-many class="restaurant.meal.MealIngredient" />
</set>
</class>
<class name="restaurant.meal.MealIngredient" table="MealIngredients">
<id name="id" type="integer">
<column name="id" not-null="true" unique="true"/>
<generator class="increment"/>
</id>
<many-to-one name="ingredient" column="ingredientId" not-null="true" class="restaurant.storage.Ingredient" lazy="false" />
<many-to-one name="meal" column="mealId" not-null="true" class="restaurant.meal.Meal" lazy="false" />
<!-- additional properties -->
</class>
I have three tables:
offers; offer_groups; offer_group_members.
The offers and offer_groups tables are mapped with hibernate (see below).
In offer_group_members, I store to which group the offers belong (offer primary key, offer group primary key).
I am kinda new to hibernate so my question is: How can I get all the OfferGroups from the OFFER_GROUP_MEMBERS table based on the Offer key?
I tried something like this:
Criteria crit;
crit = getSession().createCriteria(Offer.class);
crit = crit.createCriteria("offerGroups");
crit.add(eq("key", offerKey));
Here are the mappings:
for offer:
<composite-id name="comp_id"
class="com.infonova.psm.hibernate.prodsrv.OfferPK">
<key-property name="key" column="KEY"
type="java.lang.String" length="128">
</key-property>
</composite-id>
for offer_group_key:
<id name="key" type="java.lang.String" column="KEY" length="128">
<generator class="assigned"/>
</id>`
for offer_group_key:
<set name="offers" table="OFFER_GROUP_MEMBERS" lazy="true" inverse="false"
cascade="none">
<key>
<column name="OFFER_GROUP_KEY"/>
</key>
<many-to-many class="Offer">
<column name="OFFER_KEY"/>
</many-to-many>
</set>
for offer:
<set name="offerGroups" table="OFFER_GROUP_MEMBERS"
inverse="true" lazy="true" cascade="none">
<key>
<column name="OFFER_KEY" />
</key>
<many-to-many
class="OfferGroup">
<column name="OFFER_GROUP_KEY" />
</many-to-many>
</set>
It would be easier if you showed us the entities, since it's on them that HQL and criteria queries work.
Anyway, in HQL:
select og from Offer o
inner join o.offerGroups og
where o.key = :key
And in Criteria, unfortunately, IIRC, all you can do is select the root entity or scalars, so it's hard to do this without having a bidirectionall association. If you had a bidirectional association, you could do
Criteria c = session.createCriteria(OfferGroup.class, "og");
c.createAlias("og.offers", "o");
c.add(Restrictions.eq("o.key", key));
Since you don't have the bidirectional association, the only way that I know of is to do this:
Criteria c = session.createCriteria(OfferGroup.class, "og");
DetachedCriteria dc = DetachedCriteria.forClass(Offer.class, "o");
dc.createAlias("o.offerGroups", "og2");
dc.add(Restrictions.eq("o.key", key));
dc.setProjection(Projections.property("og2.id"));
c.add(Subqueries.propertyIn("og.id", dc));
which corresponds to this ugly HQL query:
select og from OggerGroup og
where og.id in (select og2.id from Offer o
inner join o.offerGroups og2
where o.key = :key)
For such simple static queries, I don't see any reason to go with Criteria rather than HQL.
There is a Java SE project with Hibernate ORM. I feel that the problem is trivial, but need some help.
There is a code snippet:
SessionFactory factory = new Configuration().configure().buildSessionFactory();
Session s = factory.openSession();
int id = 1;
ExperimentSetResult experimentSetResult = (ExperimentSetResult)s.get(ExperimentSetResult.class, id);
System.out.println("size: " + experimentSetResult.getExperimentResults().size());
System.out.println("id[0]: " + experimentSetResult.getExperimentResults().get(0).getId());
I get a NullPointerException for the last string of code (when accessing the 0-th element of a collection associated with an object loaded recently).
There are the hbm files snippets:
ExperimentResult.hbm.xml:
<hibernate-mapping>
<class name="rmocommon.driverreaction.ExperimentResult" table="experiment_results">
<id name="id" type="int">
<generator class="increment"/>
</id>
<many-to-one class="rmocommon.driverreaction.ExperimentSetResult" name="ExperimentSetResult" column="ExperimentSetResultId" not-null="true" />
</class>
</hibernate-mapping>
ExperimentSetResult.hbm.xml:
<hibernate-mapping>
<class name="rmocommon.driverreaction.ExperimentSetResult" table="experiment_set_results">
<id name="id" type="int">
<generator class="increment"/>
</id>
<list name="ExperimentResults" cascade="all-delete-orphan" inverse="true">
<key column="ExperimentSetResultId" not-null="true"/>
<list-index column="Id"/>
<one-to-many class="rmocommon.driverreaction.ExperimentResult"/>
</list>
</class>
</hibernate-mapping>
What's wrong with mapping or with my source code?
UPDATE:
Here is an output and a stack trace:
Hibernate: select experiment0_.id as id4_2_, experiment0_.StartedDate as StartedD2_4_2_, experiment0_.FinishedDate as Finished3_4_2_, experiment0_.DeviceOutput as DeviceOu4_4_2_, person1_.id as id0_0_, person1_.Login as Login0_0_, person1_.LastName as LastName0_0_, person1_.Patronymic as Patronymic0_0_, person1_.FirstName as FirstName0_0_, person1_.Age as Age0_0_, experiment2_.id as id1_1_, experiment2_.TestMode as TestMode1_1_, experiment2_.TransportType as Transpor3_1_1_, experiment2_.TransportStartSpeed as Transpor4_1_1_, experiment2_.RoadType as RoadType1_1_, experiment2_.RoadLength as RoadLength1_1_, experiment2_.DirectionLeft as Directio7_1_1_, experiment2_.RespondToFirstEffort as RespondT8_1_1_, experiment2_.SoundOnFirstEffort as SoundOnF9_1_1_, experiment2_.ScaleObjects as ScaleOb10_1_1_, experiment2_.ShowTransportSpeed as ShowTra11_1_1_, experiment2_.BarrierXMin as Barrier12_1_1_, experiment2_.BarrierXMax as Barrier13_1_1_, experiment2_.ReactionTime as Reactio14_1_1_, experiment2_.SoundOnBarrierAppearance as SoundOn15_1_1_, experiment2_.AllowedCheatCount as Allowed16_1_1_ from experiment_set_results experiment0_ left outer join persons person1_ on experiment0_.id=person1_.id left outer join experiment_set_settings experiment2_ on experiment0_.id=experiment2_.id where experiment0_.id=?
Hibernate: select experiment0_.ExperimentSetResultId as Experim11_4_1_, experiment0_.id as id1_, experiment0_.Id as Id1_, experiment0_.id as id2_0_, experiment0_.Distance as Distance2_0_, experiment0_.Crash as Crash2_0_, experiment0_.BrakingStarted as BrakingS4_2_0_, experiment0_.BrakingStartedTime as BrakingS5_2_0_, experiment0_.BrakingStartedDistance as BrakingS6_2_0_, experiment0_.BarrierX as BarrierX2_0_, experiment0_.Number as Number2_0_, experiment0_.Time as Time2_0_, experiment0_.Valid as Valid2_0_, experiment0_.ExperimentSetResultId as Experim11_2_0_ from experiment_results experiment0_ where experiment0_.ExperimentSetResultId=?
size: 6
Exception in thread "main" java.lang.NullPointerException
at hibernateTest.HibernateTest.main(HibernateTest.java:45)
Java Result: 1
Might be that your mapping is wrong. Your list-index column definetly should not be ID.
If you really need the ordering, you better create a separate column for that, otherwise you will encounter problems.
Another thing I've noticed. You don't have to specify the inverse on the one-to-many relationship.
It's been a while I've seen hbm.xml files, can you use annotations? They are much easier to understand.
Ok so I'm having bit of a problem with my Hibernate mappings and getting the desired behavior.
Basically what I have is the following Hibernate mapping:
<hibernate-mapping>
<class name="com.package.Person" table="PERSON" schema="MYSCHEMA" lazy="false">
<id name="personId" column="PERSON_ID" type="java.lang.Long">
<generator class="sequence">
<param name="sequence">PERSON_ID_SEQ</param>
</generator>
</id>
<property name="firstName" type="string" column="FIRST_NAME">
<property name="lastName" type="string" column="LAST_NAME">
<property name="age" type="int" column="AGE">
<set name="skills" table="PERSON_SKILL" cascade="all-delete-orphan">
<key>
<column name="PERSON_ID" precision="12" scale="0" not-null="true"/>
</key>
<many-to-many column="SKILL_ID" unique="true" class="com.package.Skill"/>
</set>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.package.Skill" table="SKILL" schema="MYSCHEMA">
<id name="skillId" column="SKILL_ID" type="java.lang.Long">
<generator class="sequence">
<param name="sequence">SKILL_ID_SEQ</param>
</generator>
</id>
<property name="description" type="string" column="DESCRIPTION">
</class>
</hibernate-mapping>
So lets assume that I have already populated the Skill table with some skills in it. Now when I create a new Person I want to associate them with a set of skills that already exist in the skill table by just setting the ID of the skill. For example:
Person p = new Person();
p.setFirstName("John");
p.setLastName("Doe");
p.setAge(55);
//Skill with id=2 is already in the skill table
Skill s = new Skill()
s.setSkillId(2L);
p.setSkills(new HashSet<Skill>(Arrays.asList(s)));
PersonDao.saveOrUpdate(p);
If I try to do that however I get an error saying:
WARN (org.slf4j.impl.JCLLoggerAdapter:357) - SQL Error: 1407, SQLState: 72000
ERROR (org.slf4j.impl.JCLLoggerAdapter:454) - ORA-01407: cannot update ("MYSCHEMA"."SKILL"."DESCRIPTION") to NULL
ERROR (org.slf4j.impl.JCLLoggerAdapter:532) - Could not synchronize database state with session
org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
The reason I am getting this error I think is because Hibernate sees that the Skill with Id 2 has 'updated' its description to null (since I never set it) and tries to update it. But I don't want Hibernate to update this. What I want it to do is insert the new Person p and insert a record into the join table, PERSON_SKILL, that matches p with the skill in the SKILL table with id=2 without touching the SKILL table.
Is there anyway to achieve this behavior?
Instead of creating the Skill object yourself:
//Skill with id=2 is already in the skill table
Skill s = new Skill()
s.setSkillId(2L);
p.setSkills(new HashSet<Skill>(Arrays.asList(s)));
You should be retrieving it from the Hibernate Session:
Skill s = (Skill) session.get(Skill.class, 2L);
p.setSkills(new HashSet<Skill>(Arrays.asList(s)));
This way the Session thinks that the skill contained in p.skills is persistent, and not transient.
This may be possible if you don't cascade all-delete-orphan which is explicitely telling hibernate to cascade the changes.
But the right way would be IMO to load load the desired Skill entity from the database and to add it to the set of skills of the Person.