I'm wondering why hibernate generates 1 delete per entity on a child table
instead of using one delete on the foreign key
Here's the hibernate.cfg.xml (No i's not the next SO :-t
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.url">jdbc:hsqldb:file:testdb;shutdown=true</property>
<property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.connection.pool_size">0</property>
<property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hbm2ddl.auto">auto</property>
<mapping file="entities/Question.hbm.xml"/>
<mapping file="entities/Answer.hbm.xml"/>
</session-factory>
Question.hbm.xml
<hibernate-mapping>
<class name="entities.Question">
<id name="id">
<generator class="native" />
</id>
<property name="title" not-null="true">
</property>
<property name="question" type="text" not-null="true">
</property>
<bag name="answers" inverse="true" cascade="all,delete-orphan" >
<key>
<column name="questionId" index="answer_questionId_idx" not-null="true"/>
</key>
<one-to-many class="entities.Answer" />
</bag>
<property name="created" update="false" >
<column name="created" not-null="true" index="answer_created_idx"></column>
</property>
<property name="lastUpdated">
<column name="lastUpdated" not-null="true" index="answer_lastUpdated_idx"></column>
</property>
</class>
</hibernate-mapping>
Answer.hbm.xml
<hibernate-mapping>
<class name="entities.Answer">
<id name="id">
<generator class="native" />
</id>
<property name="answer" type="text" not-null="true">
</property>
<property name="created" update="false" >
<column not-null="true" name="created" index="question_created_idx"></column>
</property>
<property name="lastUpdated" >
<column name="lastUpdated" not-null="true" index="question_lastUpdated_idx"></column>
</property>
<many-to-one name="question" column="questionId" not-null="true" update="false">
</many-to-one>
</class>
</hibernate-mapping>
There's 1 Question and 2 answers in my database, this test code:
Session session = factory.openSession();
Transaction t = session.beginTransaction();
Question q = (Question) session.load(Question.class,1);
session.delete(q);
t.commit();
session.close();
I would expect it to generate SQL like,
select .... from Questions where id = 1;
delete from Answers where questionId=1;
delete from Question where id=1;
I.e., just issue one delete to do the cascading delete on Answers,
instead it's loading all the answers and issuing one delete per answer, like:
select
question0_.id as id0_0_,
question0_.title as title0_0_,
question0_.question as question0_0_,
question0_.created as created0_0_,
question0_.lastUpdated as lastUpda5_0_0_
from
Question question0_
where
question0_.id=?
select
answers0_.questionId as questionId0_1_,
answers0_.id as id1_,
answers0_.id as id1_0_,
answers0_.answer as answer1_0_,
answers0_.created as created1_0_,
answers0_.lastUpdated as lastUpda4_1_0_,
answers0_.questionId as questionId1_0_
from
Answer answers0_
where
answers0_.questionId=?
delete from Answer where id=?
delete from Answer where id=?
delete from Question where id=?
How come, and is there anything I can do about it ?
Edit, in response to Nate Zaugg, I can get the db to do the cascading delete by setting on-delete="cascade" on the one-to-many key mapping, i'm more wondering why hibernate does what it does and not does one delete on the Answers table, and wheter threre's something wrong with my mappings.
Can you not configure your DMBS to do cascading deletes on relationships? It's really easy to do.
Edit: Try this <one-to-many class="entities.Answer" lazy="false" cascade="all" />
Related
When hibernate tries to paginate an entity generated using the following hbm.xml, it throws up column ambiguously defined.
<class lazy="false" dynamic-update="true" optimistic-lock="all" table="A" name="org.package.Entity">
<cache usage="read-write"/>
<id unsaved-value="null" type="java.lang.Long" name="aId">
<column name="ID_A" not-null="true" sql-type="java.lang.Long"/>
<generator class="org.package.Entity"/>
</id>
<property type="java.lang.Long" name="aGroupId" not-null="true">
<column name="ID_GROUP" not-null="true" sql-type="java.lang.Long"/>
</property>
<property type="java.lang.String" name="statusCode" not-null="true">
<column name="CD_STATUS" not-null="true" sql-type="char(30)" length="30"/>
</property>
<property type="java.lang.Long" name="templateId" not-null="false">
<column name="ID_TEMPLATE" not-null="false" sql-type="java.lang.Long"/>
</property>
<many-to-one name="aGroup" cascade="none" column="Id_Group"
class="org.package.Entity"
insert="false" update="false"/>
<many-to-one name="template" cascade="none" column="ID_TEMPLATE"
class="org.package.Entity"
insert="false" update="false"/>
</class>
What is wrong with this entity definition?
Edit: Turning it into QandA format.
ID_TEMPLATE is being condensed into one column in the query, ID_GROUP isn't.
Hibernate uses a direct string comparison to see if two properties depend on the same column. This is case sensitive, so ID_GROUP was being selected a second time as Id_Group.
Changing the cases to match, it worked.
<class lazy="false" dynamic-update="true" optimistic-lock="all" table="A" name="org.package.Entity">
<cache usage="read-write"/>
<id unsaved-value="null" type="java.lang.Long" name="aId">
<column name="ID_A" not-null="true" sql-type="java.lang.Long"/>
<generator class="org.package.Entity"/>
</id>
<property type="java.lang.Long" name="aGroupId" not-null="true">
<column name="ID_GROUP" not-null="true" sql-type="java.lang.Long"/>
</property>
<property type="java.lang.String" name="statusCode" not-null="true">
<column name="CD_STATUS" not-null="true" sql-type="char(30)" length="30"/>
</property>
<property type="java.lang.Long" name="templateId" not-null="false">
<column name="ID_TEMPLATE" not-null="false" sql-type="java.lang.Long"/>
</property>
<many-to-one name="aGroup" cascade="none" column="ID_GROUP"
class="org.package.Entity"
insert="false" update="false"/>
<many-to-one name="template" cascade="none" column="ID_TEMPLATE"
class="org.package.Entity"
insert="false" update="false"/>
</class>
Maintaining old code is fun. Hope this helps someone.
My application contains both ORM and OGM. For ORM I was wrote some named queries in xyz.hbm.xml. But while using OGM those queries caused for exception. I am using OGM 4.1.3.Final version. Please help someone.
example.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 Jan 1, 2015 1:53:57 PM by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
<class name="mkcl.os.apps.edumiss.model.student.Admission" table="ADMISSION">
<id name="id" type="java.lang.String">
<column name="ADMISSION_ID" />
<generator class="uuid" />
</id>
<property name="studentId">
<column name="STUDENTID" />
</property>
<property name="schoolId">
<column name="SCHOOL_ID" />
</property>
<property name="academicYearId" type="int">
<column name="ACADEMICYEARID" />
</property>
<property name="sectionRollNumber" type="int">
<column name="SECTIONROLLNUMBER" />
</property>
<property name="admissionDate" type="java.util.Date">
<column name="ADMISSIONDATE" />
</property>
<property name="schoolLeavingDate" type="java.util.Date">
<column name="SCHOOL_LEAVING_DATE" />
</property>
<property name="standardId" type="short">
<column name="STANDARDID" />
</property>
<property name="standardName" type="java.lang.String">
<column name="STANDARD_NAME" />
</property>
<property name="sectionId">
<column name="SECTION_ID" />
</property>
<property name="sectionName">
<column name="SECTION_NAME" />
</property>
<property name="reasonForLeaving">
<column name="REASON_FOR_LEAVING" />
</property>
<property name="streamId" type="short">
<column name="STREAM_ID" />
</property>
<property name="streamName" type="java.lang.String">
<column name="STREAM_NAME" />
</property>
<property name="admissionType" column="ADMISSION_TYPE">
<type name="org.hibernate.type.EnumType">
<param name="useNamed"></param>
<param name="enumClass">mkcl.os.apps.edumiss.model.student.AdmissionType</param>
</type>
</property>
<property name="createdBy" type="java.lang.String">
<column name="CREATED_BY" />
</property>
<property name="createOn" type="java.util.Date">
<column name="CREATED_ON" />
</property>
<property name="modifiedBy" type="java.lang.String">
<column name="MODIFIED_BY" />
</property>
<property name="lastModified" type="java.util.Date">
<column name="LAST_MODIFIED" />
</property>
<property name="patternId" type="integer">
<column name="PATTERN_ID"></column>
</property>
</class>
<sql-query name="getAdmissionForAcademicYear">
<return alias="admission" class="mkcl.os.apps.edumiss.model.student.Admission"></return>
<![CDATA[
SELECT
{admission.*}
FROM
ADMISSION admission
INNER JOIN STUDENT s
ON
s.CURRENT_ADMISSION_ID = admission.ADMISSION_ID
WHERE
admission.STUDENTID = :mkclIdentificationNumber
AND
admission.ACADEMICYEARID = :academicYearId
]]>
</sql-query>
</hibernate-mapping>
this throws
java.lang.IncompatibleClassChangeError: class org.objectweb.asm.tree.ClassNode has interface org.objectweb.asm.ClassVisitor as super class
if I am removing above query from hbm file then it worked fine. But I have to keep those queries in .hbm.xml file as it is.
That query is for a relational database, so you don't need the OGM for that.
Why do you want to run an SQL query against OGM, which is for NoSQL?
The default hibernate.archive.autodetection property value is class,hbm, so make sure you set it to class in the persistence.xml file that's associated to the OGM EntityManagerFactory. You do have two separate persistence.xml configurations (one for ORM and one for OGM), right?
according to current version of OGM(5.0) feature document:
Model can not possible to share between OGM and ORM together and this will projected to resolve in next version of OGM.
ORM classes can try to parse the .hbm.xml file and find the named queries which having the syntax of mongo query. so while reading the file OGM unable to parse the syntax of SQL named query and throws an error so I have only way to shift the all the queries into another hbm whose wntry will not be present in nosql_hibernate.xml file so OGM will not parse and my code will run fast else till this feature not support shift to another framework.
I need to use many to one relationship between two tables while using the spring hibernate integrated program. I have employee.hbm.xml and admin.hbm.xml which has corresponding bean classes.
empid in employee.hbm.xml acts as a foreign key in admin.hbm.xml .
employee.hbm.xml
<hibernate-mapping>
<class name="entity.Employee" table="employee">
<id name="empid" column="empid" type="java.lang.Integer">
<generator class="native"></generator>
</id>
<property name="empname" column="empname" type="java.lang.String"></property>
<property name="password" column="password" type="java.lang.String"></property>
<property name="designation" column="designation" type="java.lang.String"></property>
<property name="domain" column="domain" type="java.lang.String"></property>
<property name="role" column="role" type="java.lang.String"></property>
<property name="head" column="head" type="java.lang.String"></property>
<property name="specialist" column="specialist" type="java.lang.String"></property>
</class>
</hibernate-mapping>
admin.hbm.xml
<hibernate-mapping>
<class name="entity.Admin" table="admin">
<id name="courseid" column="courseid" type="java.lang.Integer">
<generator class="increment"></generator>
</id>
<property name="coursename" column="coursename" type="java.lang.String"></property>
<property name="participants" column="participants" type="java.lang.Integer"></property>
<!-- <property name="empid" column="empid" type="java.lang.Integer"/> -->
<many-to-one name="employee" class="entity.Employee" fetch="select"
column="empid" cascade="all"/>
</class>
</hibernate-mapping>
while i try to insert to admin table, i get a NullPointerException on field empid. what should i modify to make foreign key constraint between these two hbm files???
Please have a look at the below XML code
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- Generated Feb 17, 2015 10:01:43 PM by Hibernate Tools 4.3.1 -->
<hibernate-mapping>
<class name="model.main.Family" table="family" catalog="****" optimistic-lock="version">
<id name="idFamily" type="int">
<column name="idFamily" />
<generator class="assigned" />
</id>
<many-to-one name="employee" class="model.main.Employee" fetch="select">
<column name="idEmployee" not-null="true" />
</many-to-one>
<property name="firstName" type="string">
<column name="FirstName" length="45" />
</property>
<property name="middleName" type="string">
<column name="MiddleName" length="45" />
</property>
<property name="lastName" type="string">
<column name="LastName" length="45" />
</property>
<property name="dob" type="date">
<column name="DOB" length="10" />
</property>
<property name="passportNumber" type="string">
<column name="PassportNumber" length="45" not-null="true" />
</property>
<property name="dateLeft" type="date">
<column name="DateLeft" length="10" />
</property>
<property name="lastUpdated" type="timestamp">
<column name="LastUpdated" length="19" not-null="true" />
</property>
<set name="visas" table="visa" inverse="true" lazy="true" fetch="select">
<key>
<column name="idFamily" />
</key>
<one-to-many class="model.main.Visa" />
</set>
</class>
</hibernate-mapping>
It is the Hibernate mapping class of my database table Family. We create the database separately using MySQL Work bench and then generate the mapping classes. We auto generated the mapping files using netbeans as mentioned in "Generating Hibernate Mapping Files and Java Classes" section of netbeans tutorial.
Now we have a problem. That is, we changed the primary key (idFamily) of our table Family to an auto generated field inside MySQL. Now, how can we change the above hibernate code so it identifies the idFamily as an auto generated one?
The other question is, manually editing one mapping class without regenerating all the mappings via a tool can "break" the system? For an example, like messing up with relationships?
In Annotation It work for me as
#GeneratedValue(strategy= GenerationType.IDENTITY)
for you hope it works
<generated-value strategy="IDENTITY" />
You're looking for an identity column. That indicates that the column value is auto-generated as an identity for the row by the RDBMS.
<generator class="identity" />
See the these Hibernate docs for more information. According to it:
Identity
supports identity columns in DB2, MySQL, MS SQL Server, Sybase and HypersonicSQL. The returned identifier is of type long, short or int.
Just replace your generator class to increment it will treat it as autoincrement
<generator class="increment"/>
<?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>
<class name="com.tech.spring4.model.User" table="Customer">
<id name="id" type="long">
<column name="USERID" unique="true"/>
<generator class="increment"/>
</id>
<property name="username"><column name="username" length="30" not-null="true"></column></property>
<property name="email"><column name="email" length="100" not-null="true"></column></property>
<property name="address"><column name="address" length="100" not-null="true"></column></property>
</class>
</hibernate-mapping>
I have a one-to-Many Set defined in my mapping file, and when I use the order-by property the data in the Set will be missing rows depending on what property I use to order.
<hibernate-mapping>
<class name="domain.StudentUser" table="student_user" >
....
<set name="studentCourseses" inverse="true" order-by="import_date ASC">
<key>
<column name="student_userid" length="15" not-null="true" />
</key>
<one-to-many class="domain.StudentCourses" />
</set>
</hibernate-mapping>
public StudentUser findById(java.lang.String id) {
log.debug("getting StudentUser instance with id: " + id);
try {
StudentUser instance = (StudentUser) getSession().get(
"domain.StudentUser", id);
return instance;
} catch (RuntimeException re) {
log.error("get failed", re);
throw re;
}
}
Here i use import_date, but this table has about 15 properties and certain properties will cause rows to be missing from the Set while others don't. It doesn't seem to be data-type specific as I have the issue with a String property but then another String property will work fine and all data will be there. It is always the same rows that are missing no matter the sorting property used, so its not random rows.
The majority of the student_user have data missing, but not all. The rows that end up missing have nothing abnormal about them, meaning no NULL's or anything like that.
As of this point, i just removed the order-by attribute so I can get my app working property, but I still need to know why these issues are happening. So far I have not noticed this behavior in any of the other Set's that i use the order-by attribute with, just this one.
I am using Hibernate 3.5.3 and in case it's helpful, here is the mapping file for the StudentCourses table
<hibernate-mapping>
<class name="domain.StudentCourses" table="student_courses" >
<id name="id" type="integer">
<column name="id" />
<generator class="native" />
</id>
<many-to-one name="classLevel" class="domain.ClassLevel" fetch="select">
<column name="class_level_code" length="2" not-null="true" />
</many-to-one>
<many-to-one name="academicTerm" class="domain.AcademicTerm" fetch="select">
<column name="academic_term_id" not-null="true" />
</many-to-one>
<many-to-one name="studentUser" class="domain.StudentUser" fetch="select">
<column name="student_userid" length="15" not-null="true" />
</many-to-one>
<property name="academicYear" type="integer">
<column name="academic_year" not-null="true" />
</property>
<property name="term" type="string">
<column name="term" length="8" not-null="true" />
</property>
<property name="title" type="string">
<column name="title" length="64" not-null="true" />
</property>
<property name="prefix" type="string">
<column name="prefix" length="8" not-null="true" />
</property>
<property name="courseNum" type="string">
<column name="course_num" length="8" not-null="true" />
</property>
<property name="suffix" type="string">
<column name="suffix" length="8" />
</property>
<property name="sectionNum" type="string">
<column name="section_num" length="8" />
</property>
<property name="units" type="double">
<column name="units" precision="4" not-null="true" unique="true" />
</property>
<property name="letterGrade" type="string">
<column name="letter_grade" length="6" not-null="true" />
</property>
<property name="qtrGpa" type="double">
<column name="qtr_gpa" precision="4" scale="3" not-null="true" />
</property>
<property name="cumGpa" type="double">
<column name="cum_gpa" precision="4" scale="3" not-null="true" />
</property>
<property name="importDate" type="timestamp">
<column name="import_date" length="19" />
</property>
<property name="sourceFileName" type="string">
<column name="source_file_name" length="128" />
</property>
<property name="timeStamp" type="timestamp">
<column name="time_stamp" length="19" not-null="true">
<comment>on update CURRENT_TIMESTAMP</comment>
</column>
</property>
</class>
Update (More Info):
If i directly query the StudentCourses table (CORRECT) vs getting the StudentCourses data from the StudentUser mapping set (INCORRECT) gives me different results, they should match
for(StudentCourses course : getStudentService().retrieveStudentByID("861043440").getStudentCourseses()) { //MISSING DATA
System.out.println(course +" - "+course.getTerm());
}
for(StudentCourses course : (List<StudentCourses>)getStudentService().getStudentCoursesDAO().findByProperty("studentUser.studentUserid", "861043440")) { //ALL DATA CORRECT
System.out.println(course +" - "+course.getTerm());
}
Turns out it had nothing to do with sorting, as adjusting the sorting only fixed the issue for some but not all. The problem was that I had overriden the hashCode method of the StudentCourses object and it was causing collisions when the Set was being populated from the StudentUser object. I fixed this and everything is good now.