I have 2 classes Tema(Homework) and Disciplina (course), where a Course has a Set of homeworks.
In Hibernate i have mapped this to a one-to-many associations like this:
<class name="model.Disciplina" table="devgar_scoala.discipline" >
<id name="id" >
<generator class="increment"/>
</id>
<set name="listaTeme" table="devgar_scoala.teme">
<key column="Discipline_id" not-null="true" ></key>
<one-to-many class="model.Tema" ></one-to-many>
</set>
</class>
<class name="model.Tema" table="devgar_scoala.teme" >
<id name="id">
<generator class="increment" />
</id>
<property name="titlu" type="string" />
<property name="cerinta" type="binary">
<column name="cerinta" sql-type="blob" />
</property>
</class>
The problem is that it will add (insert rows in the table 'Teme') but it won't delete any rows and i get no exceptions thrown.
Im using the merge() method.
Although your question is unclear (how do you save and delete?), I'd suggest you need to set cascade:
<set cascade="all-delete-orphan">
As a sidenote - avoid names in your native language.
According to your description, I understand that a Tema cannot exist without its Disciplina: if you remove a Tema from the collection, you want it to be deleted. To tell Hibernate to do this, you must use cascade="all-delete-orphan".
<set name="listaTeme" table="devgar_scoala.teme" cascade="all-delete-orphan">
<key column="Discipline_id" not-null="true" ></key>
<one-to-many class="model.Tema" ></one-to-many>
</set>
Refer to the online documentation.
Related
I am using Hibernate 5.6 and have user Profile with Groups, which have Columns, which have ColumnProperties. I'd like to clone a column of a profile and attach it to the column group.
Profile -> Groups -> Columns -> ColumnProperties
I get this error:
10:58:39,793 ERROR
[com.myApp.core.rmgt.profile.service.RmgtProfileServiceImpl] (default
task-2) javax.persistence.EntityExistsException: A different object
with the same identifier value was already associated with the session
:
[com.myApp.core.rmgt.profile.business.object.RmgtColumnPropertyBVOImpl#124320]
Here's my mapping files:
<hibernate-mapping>
<class name="com.myapp.core.rmgt.profile.business.object.RmgtProfileBVOImpl" table="rmgt_t_profile" proxy="com.myapp.core.rmgt.profile.common.business.object.RmgtProfileBVO">
<id name="rowguid" column="rowguid" type="java.lang.Long">
<generator class="sequence">
<param name="sequence_name">rmgt_t_profile_rowguid_seq</param>
</generator>
</id>
...
<set name="groups" table="rmgt_t_group" lazy="false" cascade="all-delete-orphan" order-by="rmgtg_group_order asc">
<key column="rmgtg_fk_profile_id" not-null="true"/>
<one-to-many class="com.myapp.core.rmgt.profile.business.object.RmgtGroupBVOImpl"/>
</set>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.myapp.core.rmgt.profile.business.object.RmgtGroupBVOImpl" table="rmgt_t_group" proxy="com.myapp.core.rmgt.profile.common.business.object.RmgtGroupBVO">
<id name="rowguid" column="rowguid" type="java.lang.Long">
<generator class="sequence">
<param name="sequence_name">rmgt_t_group_rowguid_seq</param>
</generator>
</id>
...
<set name="columns" table="rmgt_t_column" lazy="false" cascade="all-delete-orphan" order-by="rmgtc_column_order asc">
<key column="rmgtc_fk_group_id" not-null="true"/>
<one-to-many class="com.myapp.core.rmgt.profile.business.object.RmgtColumnBVOImpl"/>
</set>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.myapp.core.rmgt.profile.business.object.RmgtColumnBVOImpl" table="rmgt_t_column" proxy="com.myapp.core.rmgt.profile.common.business.object.RmgtColumnBVO">
<id name="rowguid" column="rowguid" type="java.lang.Long">
<generator class="sequence">
<param name="sequence_name">rmgt_t_column_rowguid_seq</param>
</generator>
</id>
...
<set name="columnProperties" table="rmgt_t_column_property" lazy="false" cascade="all-delete-orphan" order-by="rmgtcp_order asc">
<key column="rmgtcp_fk_column_id" not-null="true"/>
<one-to-many class="com.myapp.core.rmgt.profile.business.object.RmgtColumnPropertyBVOImpl"/>
</set>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.myapp.core.rmgt.profile.business.object.RmgtColumnPropertyBVOImpl" table="rmgt_t_column_property" proxy="com.myapp.core.rmgt.profile.common.business.object.RmgtColumnPropertyBVO">
<id name="rowguid" column="rowguid" type="java.lang.Long">
<generator class="sequence">
<param name="sequence_name">rmgt_t_column_property_rowguid_seq</param>
</generator>
</id>
<property name="key" type="java.lang.String" column="rmgtcp_key" not-null="true" />
<property name="value" type="java.lang.String" column="rmgtcp_value" not-null="true" />
<property name="order" type="java.lang.Integer" column="rmgtcp_order" not-null="true" />
</class>
</hibernate-mapping>
What I do is i fetch a Profile from DB, then I use Spring's BeanUtils.copyProperties() to create a copy of the column that I want to clone. I made sure to evict all columnProperties before I proceed.
RmgtColumnBVO column = columnService.findLazyById(columnId);
// Evict all properties to force hibernate write new instances to DB
column.getColumnProperties().stream().forEach(x -> profileDAO.getSession().evict(x));
RmgtColumnBVO columnClone = new RmgtColumnBVOImpl();
BeanUtils.copyProperties(column, columnClone);
columnClone.setRowguid(null);
columnClone.setProfileId(profileId);
columnClone.setGroupId(groupId);
Then I null all rowguids of columnProperties objects of the columnClone:
// clone properties
Set<RmgtColumnPropertyBVO> propertys = column.getColumnProperties();
Set<RmgtColumnPropertyBVO> propertysClone = new HashSet<RmgtColumnPropertyBVO>();
for (RmgtColumnPropertyBVO property : propertys) {
RmgtColumnPropertyBVO propertyClone = new RmgtColumnPropertyBVOImpl();
BeanUtils.copyProperties(property, propertyClone);
propertyClone.setRowguid(null);
propertysClone.add(propertyClone);
}
columnClone.setColumnProperties(propertysClone);
After that, I fetch the profile and attach the cloned column to it and save the profile again:
RmgtProfileBVO profile = profileService.findById(column.getProfileId());
profile.getGroup(groupId).getColumns().add(columnClone);
profileService.saveOrUpdate(profile, object.getCreationUser());
What can I do to fix the problem? I don't understand why Hibernate is still complaining about the properties when I evicted every original one.
I found a solution for my problem. I replaced the profileService.saveOrUpdate() with profileService.merge() which did the job (although I do not fully understand why saveOrUpdate() does not).
In my opinion, saveOrUpdate should
Check if profile exists and check if there are changes (new elements in collections, new attribute values).
Check for every mapped collection (in my case: columns) if there are attribute changes or new items
Traverse the mapping tree and check for every mapped collection (columnProperties) of a mapped collection (columns) if value changes or new items (which was the case for me) and update/insert accordingly.
The error I get is " org.hibernate.MappingException: Repeated column in mapping for entity: cdd.model.Answer column: answer_id (should be mapped with insert="false" update="false") ". However when I put those as attributes I get the error: "Attribute "insert" must be declared for element type "id"."
Any help would be appreciated.
Class:
public class Answer {
UUID answerID;
String content;
//constructors and getters and setters
}
Table:
CREATE TABLE IF NOT EXISTS answer (
answer_id uuid NOT NULL ,
content text NOT NULL,
primary key(answer_id)
);
Hibernate Mapping File:
<!-->==== Answer ====<!-->
<class name="cdd.model.Answer" table="answer" >
<id column="answer_id" name="answerID"
type="org.hibernate.type.PostgresUUIDType"
insert="false" update="false">
<generator class="org.hibernate.id.UUIDGenerator"/>
</id>
<property column="content" name="content" type="org.hibernate.type.TextType"/>
</class>
Note
I have a Question class where a question has a set of answers. This is the mapping I used. I am posting it because the error I'm gettng may be because of how I mapped this one-to-many relationship (I'm not sure).
<!-- ==== Question ==== -->
<class name="cdd.model.Question" table="question">
<id column="question_id" name="questionID" type="org.hibernate.type.PostgresUUIDType">
< generator class="org.hibernate.id.UUIDGenerator"/>
</id>
<many-to-one column="submitted_by" name="submittedBy" not-null="true"/>
<many-to-one column="parentCategory" name="parentCategory" not-null="true"/>
<property column="title" name="title" type="org.hibernate.type.TextType"/>
<property column="correct_answer" name="correctAnswer" type="org.hibernate.type.TextType"/>
<property column="date_submitted" name="dateSubmitted" type="org.hibernate.type.TimestampType"/>
<set cascade="all" name="answers" table="answer">
<key column="answer_id" not-null="true"/>
<one-to-many class="cdd.model.Answer"/>
</set>
<join inverse="true" optional="true" table="category_questions">
<key column="question_id"/>
</join>
<join inverse="true" optional="true" table="accepted_questions_by_user">
<key column="question_id"/>
</join>
</class>
Question Entity:
<id column="question_id" name="questionID" type="org.hibernate.type.PostgresUUIDType">
<generator class="org.hibernate.id.UUIDGenerator"/>
</id>
Answer Entity:
<id column="answer_id" name="answerID" type="org.hibernate.type.PostgresUUIDType"
insert="false" update="false">
<generator class="org.hibernate.id.UUIDGenerator"/>
</id>
remove insert="false" update="false" from Answr entity
should look someting like this
<id column="answer_id" name="answerID" type="org.hibernate.type.PostgresUUIDType" >
<generator class="org.hibernate.id.UUIDGenerator"/>
</id>
update, insert (optional - defaults to true): specifies that the mapped columns should be included in SQL UPDATE and/or INSERT statements. Setting both to false allows a pure "derived" property whose value is initialized from some other property that maps to the same column(s), or by a trigger or other application.
Hello Stackeroverflowers,
i'd like to ask how i can solve the following problem:
I have 3 Tables:
Hardware
PC
Software
They have a Many-to-Many Relation. So N Hardware Entries can have M Hardware Entries.
When im Calling my Hibernate stuff then i get all Pc's with the chosen Software. In Software i have a Mappign on Hardware to get the specified Hardware of a Pc.
So far so good.
The Problem im facing is that i have to make this compatible from the other side to allow to get all Pc's with the specified Hardware and then from the Pc's the software.
When i have a mapping that links from Software over Pc to Hardware its ok. When i put a mapping into Hardware to get Pc's. Im getting a Stackoverflow because Hibernate tries to create everytime i initialize a Hardware to initialize a Pc and Pc tries then to initialize a Hardware so i get a Loop that never ends.
Can someone give me a hint to Solve this problem ?
I heard of that the attribute inverse can solve this but i dont know where ro place it and how it works.
I'm thankful for every Comment.
Hardware.hbm.xml
<?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 04.11.2013 17:30:12 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
<class name="de.test.database.pojo.Hardware" table="object" schema="XXX">
<id name="id" type="int">
<column name="id" />
<generator class="assigned" />
</id>
<property name="fkserialId" type="int">
<column name="fk_serial_id" not-null="true" />
</property>
<property name="name" type="string">
<column name="name_id" not-null="true" />
</property>
<set name="linkHardwareToSoftware" table="pc_link" inverse="true" lazy="true" fetch="select">
<key foreign-key="none">
<column name="fk_serial_id" not-null="true" />
</key>
<one-to-many class="de.test.database.pojo.PC" />
</set>
</class>
</hibernate-mapping>
PC.hbm.xml
<hibernate-mapping>
<class name="de.test.database.pojo.pc" table="pc_link" schema="xxx">
<id name="id" type="int">
<column name="id" />
<generator class="assigned" />
</id>
<property name="sort" type="int">
<column name="sort" not-null="true" />
</property>
<property name="owner" type="string">
<column name="owner" not-null="true" />
</property>
<many-to-one name="hardware" class="de.test.database.pojo.hardware" fetch="select">
<column name="fk_serial_id" not-null="true" />
</many-to-one>
<many-to-one name="software" class="de.test.database.pojo.Software" fetch="select">
<column name="fk_sw_id" not-null="true" />
</many-to-one>
</class>
</hibernate-mapping>
Software.hbm.xml
<hibernate-mapping>
<class name="de.test.database.pojo.Software" table="object" schema="xxx">
<id name="id" type="int">
<column name="id" />
<generator class="assigned" />
</id>
<property name="fkswId" type="int">
<column name="fk_sw_id" not-null="true" />
</property>
<property name="name" type="java.lang.Integer">
<column name="name" />
</property>
<property name="company" type="java.lang.Integer">
<column name="company" />
</property>
<set name="linkSWToHardware" table="pc_link" inverse="true" lazy="true" fetch="select">
<key foreign-key="none">
<column name="fk_sw_id" not-null="true" />
</key>
<one-to-many class="de.test.database.pojo.pc" />
</set>
</class>
</hibernate-mapping>
HibernateCode.java
try
{
String obj =" AND t.fkHWtypeId =:otid";
if(objectType==0)
obj="";
Session ses = getSession();
Query query = ses.createQuery(
" FROM hardware t"+
" WHERE t.deleted = 0 AND t.Id =:pid"+obj
);
query.setParameter("pid", HardwareId);
if(objectType!=0){
System.out.println("Reading HWtypeid...");
query.setParameter("HWtypeid", HardwareType);
}
List<Tree> list = query.list();
return list;
} catch (HibernateException e)
{
return null;
}
Stacktrace:
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: de.test.database.pojo.Pc["hardware"]->de.test.database.pojo.Hardware_$$_javassist_109["linkHardwareToSoftware"]->org.hibernate.collection.internal.PersistentSet[0]->de.test.database.pojo.pc["object"]->de.test.database.pojo.Hardware_$$_javassist_109["linkHardwareToSoftware"]
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:164)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at org.codehaus.jackson.map.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:72)
at org.codehaus.jackson.map.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:23)
at org.codehaus.jackson.map.ser.std.AsArraySerializerBase.serialize(AsArraySerializerBase.java:86)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:150)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:150)
As the stack trace indicates, the problem has nothing to do with the mapping and with loading data from the database with Hibernate. The problem happens when you're serializing your beans with Jackson, because you have bidirectional associations and thus cyclic references which cause Jackson to loop endlessly.
So, you should choose how you want to serialize your objects, and use Jackson annotations or use DTOs to serialize them and break the cycles between objects. You could, for example, add a #JsonIgnore on the linkHardwareToSoftware field, so that the collection of software is not serialized when serializing a Hardware instance.
I'm using three java objects that are mapped with Hibernate with this pattern :
TaskModel.class -> 1:N -> TaskModelPropertiesGroup.class -> 1:N -> TaskModelProperty.class
At the project deployment I get this exception :
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
Yes, I've done my homework, I've seen couple of answers about that, but no one about how to modify hbm.xml files according.
1 #LazyCollection(LazyCollectionOption.FALSE)
Don't know how to change that in my mappings since I am already using lazy="false".
2 Changing List to Set
I've tried to change List<> to Set<> inside my classes but I don't think this is the way...
Here are my mappings :
<hibernate-mapping>
<class dynamic-insert="false" dynamic-update="false" mutable="true" name="com.spectotechnologies.website.projects.helper.TaskModel" entity-name="com.spectotechnologies.website.projects.helper.TaskModelComplete" optimistic-lock="version" polymorphism="implicit" select-before-update="false" table="projects_tasksModels">
<id name="keyTaskModel">
<generator class="native"/>
</id>
<property name="name"/>
<property name="description"/>
<!-- Complete -->
<bag name="propertiesGroups" table="projects_tasksModelsPropertiesGroups" lazy="false" fetch="join" cascade="all">
<key column="keyTaskModel"/>
<one-to-many class="com.spectotechnologies.website.projects.helper.TaskModelPropertiesGroupComplete" />
</bag>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class dynamic-insert="false" dynamic-update="false" mutable="true" name="com.spectotechnologies.website.projects.helper.TaskModelPropertiesGroup" entity-name="com.spectotechnologies.website.projects.helper.TaskModelPropertiesGroupComplete" optimistic-lock="version" polymorphism="implicit" select-before-update="false" table="projects_tasksModelsPropertiesGroups">
<id name="keyTaskModelPropertiesGroup">
<generator class="native"/>
</id>
<property name="keyTaskModel"/>
<property name="name"/>
<property name="description"/>
<!-- Complete -->
<bag name="properties" table="projects_tasksModelsProperties" lazy="false" fetch="join" cascade="all">
<key column="keyTaskModelPropertiesGroup"/>
<one-to-many class="com.spectotechnologies.website.projects.helper.TaskModelProperty" />
</bag>
</class>
</hibernate-mapping>
I have one-to-many relationship between parent and child Java objects. The parent object uses java.util.List that stores multiple child objects. The problem I am experiencing is when updating the parent object after I have added one or more child object(s) to the List in the parent. I am using the saveOrUpdate method to save or update the parent. It works fine if I am saving a new instance of the parent, but after saving it, I try to add child object(s) into the parent List and then attempt to call saveOrUpdate on the parent object, but no entries of child object(s) get persisted into the database. I just would like some pointers. Note: I am not using annotations. A snippet of the Parent.hbm.xml, that defines the one-to-many unidirectional relationship:
<list name="children" cascade="all">
<key column="parent_id"/>
<index column="idx"/>
<one-to-many class="Child"/>
</list>
I just tried to reproduce this example and it worked OK for me.
Here are my mappings:
<hibernate-mapping package="com.example.domain">
<class name="com.example.domain.Parent" table="PARENT">
<id name="id" column="parent_id" access="field">
<generator class="increment" />
</id>
<property name="name" column="parent_name" access="field" />
<list name="children" access="field" cascade="all">
<key column="parent_id" not-null="true" />
<index column="idx" />
<one-to-many class="Child" />
</list>
</class>
</hibernate-mapping>
<hibernate-mapping package="com.example.domain">
<class name="com.example.domain.Child" table="CHILD">
<id name="id" column="child_id" access="field">
<generator class="increment" />
</id>
<property name="name" column="child_name" access="field" />
</class>
</hibernate-mapping>
I added not-null="true" to the parent mapping.
Did you try to set show_sql in your hibernate config to see generated SQL?