I have Country and State objects. I intend to have unidirectional many to one relationship from State to Country. I don't want to store any references to States in Country I have defined mapping as below. When I delete even one State object, all Countries are deleted!
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class
name="places.Country"
table="COUNTRY"
dynamic-update="true">
<!-- Technical ID -->
<id name="name" type="string" unsaved-value="new" column="COUNTRY_NAME">
</id>
<!-- Properties -->
<property name="commonName"
column="COMMON_NAME"
/>
<property name="iso2Code"
column="ISO2_CODE"
/>
<property name="iso3Code"
column="ISO3_CODE"
/>
<property name="isoNumeric"
column="ISO_NUMERIC"
/>
<property name="ituCode"
column="ITU_CODE"
/>
<property name="ianaCode"
column="IANA_CODE"
/>
</class>
<class
name="places.State"
table="STATE"
dynamic-update="true">
<!-- Technical ID -->
<id name="name" type="string" unsaved-value="new" column="STATE_NAME">
</id>
<!-- Properties -->
<property name="code" column="STATE_CODE"
/>
<many-to-one name="country" column="COUNTRY" not-null="true" cascade="none"
class="places.Country"
/>
</class>
</hibernate-mapping>
The provided mapping looks fine. Actually, executing the following code using exactly your mapping:
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
State aState = (State) session.load(State.class, stateId);
session.delete(aState);
session.getTransaction().commit();
Generates the following output:
...
Hibernate: select state0_.STATE_NAME as STATE1_1_0_, state0_.STATE_CODE as STATE2_1_0_, state0_.COUNTRY as COUNTRY1_0_ from STATE state0_ where state0_.STATE_NAME=?
Hibernate: delete from STATE where STATE_NAME=?
3270 [main] INFO org.hibernate.impl.SessionFactoryImpl - closing
Things are working as expected, my countries are still there.
Maybe show some code?
Related
I downloaded and installed the source code for the Perseus Project (Open Source Code) on to Ubuntu 14.04, and Tomcat 6. The project hasn't been modified in years, so it must be some new feature in hibernate that is doing this.
I see the solutions here on very many threads, but they address the issue of debugging code as it is being written as opposed to migrating a project that quite certainly worked "at one time". The code that I am working with hasn't been modified since 2008-2011. (I've determined that they are running it now on "Mandriva Linux 2010.2") Therefore, I need to be able to fix this issue on a large scale - either a search/replace method or a configuration file change. I have never used hibernate myself, so I don't understand the full meaning of the posted answers.
Thanks.
Also, the files don't use the # annotated syntax, but the mappings seem to be done via XML files. At this point, I would not mind just to update all these files if I knew what to change.
<?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="perseus.document.dao">
<class name="HibernateChunkDAO$ChunkRow" table="hib_chunks" batch-size="10">
<id name="id" type="int" column="id" unsaved-value="null">
<generator class="native" />
</id>
<property name="documentID" column="document_id" type="string"
length="50" index="doc_idx doc_type_idx doc_type_value_idx
doc_cid_idx doc_so_eo_idx doc_so_eo_type_idx doc_dq_idx
doc_so_eo_type_val_idx" />
<property name="elementName" column="element" type="string"
length="20" />
<property name="type" column="type" type="string" length="30"
index="doc_type_idx doc_type_value_idx doc_so_eo_type_idx
doc_so_eo_type_val_idx" />
<property name="value" column="value" type="string" length="250"
index="doc_type_value_idx doc_so_eo_type_val_idx" />
<property name="position" column="position" type="int" />
<property name="absolutePosition" column="abs_position" type="int" />
<property name="chunkID" column="chunk_id" type="string"
length="255" index="doc_cid_idx" />
<property name="openTags" column="open_tags" type="text" />
<property name="closeTags" column="close_tags" type="text" />
<property name="startOffset" column="start_offset" type="int"
index="doc_so_eo_idx doc_so_eo_type_idx doc_so_eo_type_val_idx" />
<property name="endOffset" column="end_offset" type="int"
index="doc_so_eo_idx doc_so_eo_type_idx doc_so_eo_type_val_idx" />
<property name="displayQuery" column="display_query" type="string"
length="100" index="doc_dq_idx" />
<property name="head" column="head" type="text" />
<property name="headLanguage" column="head_lang" type="string"
length="10" />
<property name="hasCustomHead" column="custom_head" type="boolean" />
<set name="frequencies" inverse="true" cascade="all-delete-orphan"
lazy="true" batch-size="30">
<key column="chunk_id" on-delete="cascade" />
<one-to-many class="perseus.ie.freq.Frequency" />
</set>
<many-to-one name="lemma" column="lemma_id" cascade="all" lazy="false" />
<!--
<list name="senses" inverse="false" cascade="all-delete-orphan"
lazy="false" batch-size="30">
<key column="chunk_id" />
<list-index column="position" />
<one-to-many class="perseus.voting.Sense" />
</list>
-->
</class>
</hibernate-mapping>
This is happening due to below reason:
You have defined a association in your entity which is not yet persisted in the database and before persisting this association you are trying to flush the Session.For example you have a collection association which has collection of transient instances, so you have to persist this collection first before flushing.
For resolving this issue, you have 2 options:
Either manually persist the collection in your code by calling Session.save() method.
If you want to use tags in Hibernate XML file or want to use annotations make use of cascade="all" (for xml) or cascade=CascadeType.ALL (for annotations) on your collection association.
For example,
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.sql.index">
<class name="User">
<id name="id" type="long">
<generator class="native" />
</id>
<property name="firstName" type="string" index="IDX_FIRST_NAME" />
<property name="lastName" type="string" />
<property name="address" type="string" />
<property name="field_1" type="long" />
<property name="field_2" type="long" />
</class>
</hibernate-mapping>
If I want the field_1 and field_2 has 2 indexes description. Can I do the following thing? Or How to achieve it ?
<property name="field_1" type="long" index="idx_1,idx_2"/>
<property name="field_2" type="long" index="idx_1,idx_3"/>
The field_1 and field_2 will has 2 index for their self.
I refer the hibernate 3.6, 5.1.4.2 Property mapping with hbm.xml, it seems like the index field can be assigned by only one column.
PS,
The project is some kind old, and is maintained by many people, so I cannot use annotation syntax to add index.
I found the post and give it a try.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.sql.index">
<class name="User">
<id name="id" type="long">
<generator class="native" />
</id>
<property name="firstName" type="string" index="IDX_FIRST_NAME" />
<property name="lastName" type="string" />
<property name="address" type="string" />
<property name="field_1" type="long" index="idx_2"/>
<property name="field_2" type="long" index="idx_3"/>
</class>
<database-object>
<create>
CREATE INDEX idx_1 ON User (field_1, field_2)
</create>
<drop></drop>
</database-object>
</hibernate-mapping>
This problem can be solved by <database-object>, by writing native sql syntax to create index.
UPDATE, 2018/11
For unique constraint with multiple properties, someone has answered it.
Use properties tag
<properties name="uk1" unique="true">
<property name="username" .../>
<many-to-one name="client" .../>
</properties>
<properties name="uk2" unique="true">
<property name="email" .../>
<many-to-one name="client" update="false" insert="false" .../>
</properties>
I have two entities and two value objects - Employee, Card, Employee Number & Card Number. The relationship between Employee and Card is a one-to-many. I create an instance of Employee and an instance of Card like so and save them to the database...
EmployeeRepositoryHibernate employeeRepository = new EmployeeRepositoryHibernate();
employeeRepository.setSessionFactory();
employeeRepository.getSession().beginTransaction();
EmployeeNumber employeeNumber = new EmployeeNumber("MNO");
Location location = new Location("Room 1");
CardNumber cardNumber = new CardNumber("1");
Employee employee = new Employee(employeeNumber, location);
Card card = new Card(cardNumber, "1111", employee);
employeeRepository.getSession().save(employee);
employeeRepository.getSession().save(card);
employeeRepository.getSession().getTransaction().commit();
employeeRepository.getSession().close();
Except, it won't save, the following error message is shown... I can save an employee, but the message is thrown when I try to save a related card... the mysql database isn't relational yet.. both tables are separate...
Problem fixed: required related tables.
Caused by: java.sql.SQLException: Field 'employeeNumber' doesn't have a default value
Here are the two Hibernate XML mapping files for Card and Employee...
Card
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-access="field">
<class name="model.Card" table="Card">
<id name="CardID" type="long">
<column name="CardID" />
<generator class="identity" />
</id>
<component name="cardNumber" unique="true">
<property name="number" column="cardNumber"/>
</component>
<many-to-one name="employee" class="model.Employee" fetch="select">
<column name="EmpID" not-null="true"></column>
</many-to-one>
<property name="PIN" column="PIN"/>
</class>
</hibernate-mapping>
Employee
<?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 default-access="field">
<class name="model.Employee" table="employee">
<id name="EmpID" column="EmpID">
<generator class="org.hibernate.id.IdentityGenerator"/>
</id>
<component name="employeeNumber" class="model.EmployeeNumber" >
<property name="number" column="employeeNumber" type="string"/>
</component>
<component name="location">
<property name="location" column="Location" type="string"/>
</component>
<set name="cards" inverse="true" cascade="all">
<key>
<column name="EmpID" not-null="true"></column>
</key>
<one-to-many class="model.Card"/>
</set>
</class>
</hibernate-mapping>
One of these might help:
Add a default value to the column employeeNumber
ALTER TABLE 'table_name' ALTER 'employeeNumber' SET DEFAULT NULL
Use auto increment if you are using employeeNumber as a primary key.
Supply value to the employeeNumber column during insertion.
I am using Hibernate3
I have a simple one to many relationship(Parent object has as set of child objects)
if the child objects are added/removed, the version of the parent object is updated where as if the state of the child object is changed, the version of the parent is not getting updated.
Here is the mapping -
Category.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="net.codejava.hibernate">
<class name="Category" table="CATEGORY">
<id name="id" column="CATEGORY_ID">
<generator class="native"/>
</id>
<property name="name" column="NAME" />
<version name="version" type="integer" column="version" unsaved-value="null" />
<set name="products" inverse="true" cascade="all-delete-orphan">
<key column="CATEGORY_ID" not-null="true" />
<one-to-many class="Product"/>
</set>
</class>
Product.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="net.codejava.hibernate">
<class name="Product" table="PRODUCT">
<id name="id" column="PRODUCT_ID">
<generator class="native"/>
</id>
<version name="version" type="integer" column="version" unsaved-value="null" />
<property name="name" column="NAME" />
<property name="description" column="DESCRIPTION" />
<property name="price" column="PRICE" type="float" />
<many-to-one name="category" class="Category"
column="CATEGORY_ID" not-null="true"/>
</class>
When the Product changes, Product.version is updated properly but the Category.version remains the same.
I assume this is a cross cutting concern and there has to be a plausible solution for this. I did a lot of searching and could not find one. Please help me out
Yes, this is just a limitation of the way hibernate works, I'm afraid. The only solution is to change it on both sides when you need to make a change.
You could also refresh the parent, but bear in mind that that will hit the database.
It's probably a subjective opinion, but to me it seems logical this way. However, one common way of handling this is to have something like lastUpdated field on parent entity, which you would set each time before calling update on it. This can be done in #PrePersist and/or #PreUpdate, and it would ensure that entity version changes whenever you update it, regardless of what changes are made to it or its relations.
I do have a hibernate mapping which looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.lwl.anlei.bl.model.imp">
<class name="PersonFo">
<id name="id" type="integer"/>
<property name="name" type="string"/>
<property name="info" type="string"/>
</class>
<sql-query name="person1">
<return alias="gb" class="PersonFo"/>
SELECT id as {gb.id},
name as {gb.name},
info as {gb.info}
FROM table
WHERE field1 = :param
</sql-query>
<sql-query name="person2">
<return alias="gb" class="PersonFo"/>
SELECT id as {gb.id},
second_name as {gb.name},
whatever as {gb.info}
FROM table
WHERE field2 = :param
</sql-query>
</hibernate-mapping>
This is the simplyfied hbm of what I do have.
In Java I am getting my data in this way, with the "getQueryName":
List<Person> myPersons =
session.getNamedQuery("person1").setString("param", "important text").list();
Now I have to use views, instead of these querys. I know, I could simply write "SELECT xxx FROM view_person1" and "SELECT xxx FROM view_person2" into the sql-querys, but I would prefer to use something like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.lwl.anlei.bl.model.imp">
<class name="PersonFo"
table="view_person1"
entity-name="view_person1" >
<id name="id" column="id" type="integer"/>
<property name="name" column="name" type="string"/>
<property name="info" column="info" type="string"/>
</class>
<class name="PersonFo"
table="view_person2"
entity-name="view_person2" >
<id name="id" column="id" type="integer"/>
<property name="name" column="name" type="string"/>
<property name="info" column="info" type="string"/>
</class>
</hibernate-mapping>
In another example, I saw how to save data via hibernate this way.
Map Two Identical tables ( same schema...) to same entity in Hibernate
They simply used:
_session.Save("view_person1", xxxx)
_session.Save("view_person2", xxxx)
But, how do I QUERY data? Is there something like:
List<Person> myPersons =
session.getNamedEntity("view_person2").list();
Help will be very appriciated!
Thanks!
Use polymorphism="explicit" to differentiate your named entities:
<class name="PersonFo"
table="view_person1"
entity-name="view_person1" polymorphism="explicit" >
<id name="id" column="id" type="integer"/>
<property name="name" column="name" type="string"/>
<property name="info" column="info" type="string"/>
</class>
<class name="PersonFo"
table="view_person2"
entity-name="view_person2" >
<id name="id" column="id" type="integer"/>
<property name="name" column="name" type="string"/>
<property name="info" column="info" type="string"/>
</class>
and query your named entities like below:
List list1 = session.createQuery("from view_person1").list();
List list2 = session.createQuery("from view_person2").list();