Hibernate: Mixing xml and annotation-based mapping for one class - java

I have to modify (better say enhance) an already existing entity class with some metadata. The calss's metadata is defined via annotations. But now I'd like to add some UserType definitions (hibernate-specific) to the entity class. It works well if I repeat all the metadata set via annotation in my hibernate-mapping-file but is there a way to only add my specific aspect without overwriting the annotation based stuff? Thanks in advance.

Oh, sorry. Of course I can:
#Entity
public class Thing implements Serializable {
#Id
private ContractNumber contractNumber;
#Column
private String columnViaAnnotation;
public ContractNumber getContractNumber() {
return contractNumber;
}
public void setContractNumber(ContractNumber contractNumber) {
this.contractNumber = contractNumber;
}
public String getColumnViaAnnotation() {
return columnViaAnnotation;
}
public void setColumnViaAnnotation(String columnViaAnnotation) {
this.columnViaAnnotation = columnViaAnnotation;
}
}
My mapping:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-mapping>
<class name="com.mypackage.Thing">
<id name="contractNumber" type="myType" column="contractNumber" />
</class>
<typedef class="com.mypackage.HibernateContractNumberConverter" name="myType" />
</hibernate-mapping>
The custom type works perfectly well. The datatype ContractNumber will be translated into a int and vice versa. But unfortunately the column 'columnViaAnnotation' disappears due to it's not mentioned in my mapping file. I was hoping that Hibernate is smart enough to merge both configurations (xml and annotation based). Is there any chance of doing this?

Related

Hibernate unable to delete child record

I'm unable to delete a child record while I'm updating (not deleting) the parent record. Also, I've read other posts, but it seems most of the others are using annotations rather than xml, so it can be difficult to see how they relate to my issue.
I have two tables: The EventInfo table that holds information about events and then the EventLicenseType table that only has two columns and both of those columns make up the primary key; one of the columns in the EventLicenseType table is a foreign key to the EventInfo table.
The problem is I can't seem to delete an EventLicenseType record. I've tried a bunch of different things and nothing is working for me. It seems like hibernate wants to put null as the eventinfoId column, which of course doesn't work. I have tried clearing out the Set and then doing the merge, and also specifically calling session.delete(eventlicenseTypeRec) and then doing the merge. Neither is working for me.
EventInfo.hbm.xml file:
<hibernate-mapping default-lazy="true">
<class name="Eventinfo" table="PA_EVENTINFO">
<id name="eventInfoId" type="int"
column="PA_EVENTINFOID">
<generator class="native" />
</id>
<property name="eventTypeId" type="java.lang.String"
column="PA_EVENTTYPEID" length="255" />
...Other columns not shown here for brevity...
<set name="eventLicenceTypeIds" lazy="false" cascade="all-delete-orphan">
<key column="PA_EVENTINFOID"/>
<one-to-many class="EventLicenseType" />
</set>
</class>
EventLicenseType.hbm.xml file:
<hibernate-mapping default-lazy="true">
<class name="EventLicenseType" table="PA_EVENTLICENSETYPE">
<composite-id>
<key-property name="licenseTypeId" type="java.lang.Integer" column="PA_LICENSETYPE"/>
<key-property name="eventInfoId" type="java.lang.Integer" column="PA_EVENTINFOID"/>
</composite-id>
</class>
Here is the EventInfo class. Again, there are more fields in the actual file, this is just the important pieces:
public class Eventinfo implements Serializable {
/** identifier field */
private int eventInfoId;
/** nullable persistent field */
#Field(name="eventInfo_eventTypeId")
private String eventTypeId;
#IndexedEmbedded
private Set<EventLicenseType> eventLicenceTypeIds;
/** default constructor */
public Eventinfo() {}
public int getEventInfoId() {
return this.eventInfoId;
}
public void setEventInfoId(int eventInfoId) {
this.eventInfoId = eventInfoId;
}
public String getEventTypeId() {
return this.eventTypeId;
}
public void setEventTypeId(String eventTypeId) {
this.eventTypeId = eventTypeId;
}
public Set<EventLicenseType> getEventLicenceTypeIds() {
return eventLicenceTypeIds;
}
public void setEventLicenceTypeIds(Set<EventLicenseType> eventLicenceTypeIds) {
this.eventLicenceTypeIds = eventLicenceTypeIds;
}
Here is the EventLicenseType class
public class EventLicenseType implements Serializable{
#Field
private int licenseTypeId;
private int eventInfoId;
public int getLicenseTypeId() {
return licenseTypeId;
}
public void setLicenseTypeId(int licenseTypeId) {
this.licenseTypeId = licenseTypeId;
}
public int getEventInfoId() {
return eventInfoId;
}
public void setEventInfoId(int eventInfoId) {
this.eventInfoId = eventInfoId;
}
}
Here is the method I'm executing in my DAO. For now there is only one record associated to the eventInfo record, so I'm just trying to see if I can delete that one. (Also note that eventinfo is defined in the method that surrounds this one).
public Eventinfo execute(Session session) throws Exception {
//Get the existing eventInfo record
Eventinfo existing = (Eventinfo)session.get(Eventinfo.class, eventinfo.getEventInfoId());
Iterator iter = existing.getEventLicenceTypeIds().iterator();
if (iter.hasNext()) {
EventLicenseType license = (EventLicenseType) iter.next();
iter.remove();
session.delete(license);
}
session.flush();
return (Eventinfo) session.merge(eventinfo);
}
On the above session.flush() line, I get an error: java.sql.BatchUpdateException: Cannot insert the value NULL into column 'PA_EVENTINFOID', table 'PA_EVENTLICENSETYPE'; column does not allow nulls. UPDATE fails. It shows that hibernate is trying to do:
update PA_EVENTLICENSETYPE set PA_EVENTINFOID=null where PA_EVENTINFOID=?
Why can't it just delete the record? Why is it trying to do an update??
I also tried changing the code to the below and get the same error.
public Eventinfo execute(Session session) throws Exception {
//Clear out the list
eventinfo.getEventLicenceTypeIds().clear();
return (Eventinfo) session.merge(eventinfo);
}
Can anyone help me with what I'm missing, or point me in the right direction?
You need to see whether the mapping between two tables are unilateral or bilateral. Basically you need to think or the rows of two tables as objects and cut all the ties between the objects of EventInfo and EventLicenceType tables. So if the relation is only from EventInfo -> EventLicenseType, you need to set the value of the EventLicenseType set to null in EventInfo object. Also if there is a mapping from EventLicenseType, you need to set the value of joining column to null. Then merge() the EventInfo object.
Noneed to explicitly delete or remove the EventLicenseType object. If here are no references to EventLicenseType object, the JVM will collect it as garbage.
Hope that solves your problem.

Geting 'Entity class not found' error while mapping classes to database table by hibernate

Simple pojo class
package practice041116;
public class Cls {
public String name;
public int roll;
public Cls(){}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getRoll() {
return roll;
}
public void setRoll(int roll) {
this.roll = roll;
}
}
The main login file
package practice041116;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.SessionFactory;
public class MainClass {
public static void main(String args[]){
Configuration cnfg=new Configuration();
cnfg.configure("hibernate.cfg.xml");
SessionFactory fact=cnfg.buildSessionFactory();
Session session=fact.openSession();
Transaction tx=session.beginTransaction();
Cls s=new Cls();
s.setName("Taleev");
s.setRoll(23);
session.save(s);
tx.commit();
}
}
The configuration file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate
Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-
configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">
com.microsoft.sqlserver.jdbc.SQLServerDriver
</property>
<property name="hibernate.connection.url">
jdbc:microsoft:sqlserver://171.8.9.1;DatabaseName:test
</property>
<property name="hibernate.connection.username">
username
</property>
<property name="hibernate.connection.password">
password
</property>
<property name="hibernate.dialect">
org.hibernate.dialect.SQLServerDialect
</property>
<property name="show_sql">true</property>
<mapping resource="practice041116/Cls.hbm.xml" />
</session-factory>
</hibernate-configuration>
Mapping file
<?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>
<class name="Cls" table="cls">
<id name="id" column="id" type="integer"/>
<property name="name" column="NAME" type="string"/>
<property name="roll" column="ROLL" type="integer"/>
</class>
</hibernate-mapping>
I have the specified table in my database but while execute it gives Entity Class not found Exception, i have tried various answer available on the similar question such as this one
and few more from other sources from the net
Hierarchy of the project and the jar files used
Thanks in advance and forgive me if i have done some format mistake on this question as this is the first question i am posting.
Stack trace of Exception
In your mapping file :
<class name="Cls" table="cls">
you don't specify qualified class name but just simple class name.
You should replace by :
<class name="practice041116.Cls" table="cls">
Hibernate is an ORM framework which maps a Java Object (Entity Object) to a relational database table, as you did NOT map any Entity Class to a database table you are getting this exception.
Option(1): Using Annotations
You need to use #Entity (class level mapping) and #Column (element level mapping) to map the java object to a database table.
Option(2): Using mapping xml files
As sugested by David above and also you can look at here

org.hibernate.MappingException class not found while looking for property

Please help me,i really need help...
I create a composite-id in hibernate.Here are things i have
PurchasedTestId.java
package jp.go.mhlw.vaccine.draft;
import java.io.Serializable;
public class PurchasedTestId implements Serializable {
private static final long serialVersionUID = 1L;
private Long testId;
private Long customerId;
// an easy initializing constructor
public PurchasedTestId(Long testId, Long customerId) {
this.testId = testId;
this.customerId = customerId;
}
// generate setters and getters here
}
And here is my vaccin.hbm.xml file
<class name="jp.go.mhlw.vaccine.draft.PurchasedTestttt" table="PurchasedTesttt">
<composite-id name="purchasedTestId" class="jp.go.mhlw.vaccine.draft.PurchasedTestId">
<key-property name="testId" >
<column name="testId" ></column>
</key-property>
<key-property name="customerId" column="customerId" />
</composite-id>
<property name="name" column="name" type="string" />
</class>
I am using Ant build (using bulld.xml file) to generate Domain class and DB shema,only class PurchasedTestttt will be generated in my case,I've created the class PurchasedTestId before.
Whenever i start to run tools it throws
org.hibernate.MappingException: class jp.go.mhlw.vaccine.draft.PurchasedTestId not found while looking for property: testId
But in my vaccin.hbm.xml file i can hold the control key and click on
jp.go.mhlw.vaccine.draft.PurchasedTestId
And it immediately jumps to PurchasedTestId.java file with same package name.Obviously the PurchasedTestId class is in my classpath.I've been searching alot for 2 days but i could not solve my problem.Please help me figure out what it is.I am so tired
Please help me.
You don't have to specify the class of the composite-id in the hbm.xml file; you have to set the name of the property in your PurchasedTestttt class.
E.g. it has to look like:
Class PurchasedTestttt:
public class PurchasedTestttt {
PurchasedTestId purchasedTestId;
public PurchasedTestId getPurchasedTestId() {
return purchasedTestId;
}
public void setPurchasedTestId(PurchasedTestId purchasedTestId) {
this.purchasedTestId = purchasedTestId;
}
....
}
*.hbm.xml:
<class name=”entities.PurchasedTestttt”>
<composite-id name=”purchasedTestId”>
<key-property name=”testId” column=”TEST_ID” />
<key-property name=”customerId” column=”CUSTOMER_ID” />
</composite-id>
...
</class>
It is important that the class you use for the composite-id has properties with the same name as specified in *.hbm.xml, but Hibernate does not need to know the class you used for that.

Mysql inserting values

I am storing an Object into my database, and I think I am making some stupid mistake which I don't see. I attached some code to understand my problem:
MyObject.java object which I want to store in the database
#Entity
#Table(name = "my_object")
public class MyObject {
private String idName;
#Id
#Column(name = "id", unique = true, length = 50,nullable=false)
public String getIdName() {
return idName;
}
public void setIdName(String idName) {
this.idName = idName;
this.id = idName.hashCode();
}
TestToStoreObject.java
MyObject obj = new MyObject();
obj.setIdName("id");
//More set ...
save(obj);
Function to store the Object into the database
protected void save(MyObject obj) throws DataAccessLayerException {
try{
getHibernateTemplate().save(obj);
} catch (HibernateException e) {
handleException(e);
}
}
This is a general function which is working with other datatypes. So where I guess the problem is, is in configuration files. The exception is:
org.springframework.dao.InvalidDataAccessResourceUsageException:
could not insert: [path.MyObject]; SQL [insert into object (id) values (?)];
nested exception is org.hibernate.exception.SQLGrammarException:
could not insert: [path.MyObject]
Here more configuration files:
HibernatePlatform.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<mapping class="path.User" />
<mapping class="path.MyObject" />
Just a guess - you've defined a class named Object and there's a good chance, that you have name clashes between your class Object and the java class java.lang.Object. It's possible, that you try to store an java.lang.Object accidently.
For a quick test: refactor your class to MyObject (using eclipse, netbeans, ...). If some types named Object are not changed after refactoring, then my guess could be right.
The problem was that I had another class with the same name in another package, and it was messing. A piece of advise for the future: chach this small things!!

Importing and normalising XML with Hibernate

When importing xml to a DB with Hibernate, is there a way to resolve an attribute consisting of comma-separated values to populate related table(s)?
In this (somewhat obfuscated) example I have an xml file, each row of which represents a Person. The Person has a Hobbies property which contains a comma-separated list of values. The Person-Hobby relationship is many to many. In reality I have gigs of data to process.
When importing each Person to the PEOPLE table, I would like to add each Hobby to the HOBBIES table (ignoring duplicates), then add a mapping to the PEOPLE_HOBBIES table.
I've set up my mapping files with bi-directional associations and Hibernate appears to construct the tables as I'd expect (details below), however I don't see what mechanism I can use for extracting/populating the HOBBIES and PEOPLE_HOBBIES while processing PEOPLE.
All help and/or RTFM references gratefully received.
This is the file I'm processing (people.xml):
<People>
<Person Id="1" Name="Dave" Hobbies="drinking, walking"/>
<Person Id="2" Name="Geoff" Hobbies="football, ballet"/>
<Person Id="3" Name="Anne" Hobbies="walking, karate"/>
<Person Id="4" Name="Frank" Hobbies="karate, cross-stitch"/>
</People>
The Person.hbm.xml is (omitting xml decl):
<?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="name.seller.rich.hobby">
<class name="Person" node="Person" table="PEOPLE">
<id name="id" node="#Id" column="PEOPLE_ID"/>
<property name="name" node="#Name" column="NAME" type="string"/>
<property name="hobbies" node="#Hobbies" column="HOBBIES" type="string"/>
<set name="hobbiesSet" table="PEOPLE_HOBBIES">
<key column="PEOPLE_ID"/>
<many-to-many column="HOBBY" class="Hobby"/>
</set>
</class>
</hibernate-mapping>
The Hobby.hbm.xml is:
<hibernate-mapping package="name.seller.rich.hobby">
<class name="Hobby" node="Hobby" table="HOBBIES">
<id name="hobby" column="HOBBY" type="string"/>
<set name="people" table="PEOPLE_HOBBIES" inverse="true">
<key column="HOBBY"/>
<many-to-many column="PEOPLE_ID" class="Person"/>
</set>
</class>
</hibernate-mapping>
This is the Person class, in the setHobbies() method I populate the hobbiesSet with Hobby instances:
package name.seller.rich.hobby;
import java.util.HashSet;
import java.util.Set;
public class Person {
private long id;
private String name;
private String hobbies;
private Set hobbiesSet = new HashSet();
public String getHobbies() {
return hobbies;
}
public Set getHobbiesSet() {
if (hobbiesSet == null) {
hobbiesSet = new HashSet();
}
return hobbiesSet;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public void setHobbies(final String hobbies) {
this.hobbies = hobbies;
}
public void setHobbiesSet(final Set hobbiesSet) {
this.hobbiesSet = hobbiesSet;
}
public void setId(final long id) {
this.id = id;
}
public void setName(final String name) {
this.name = name;
}
}
This is the code I'm using to process the file:
package name.seller.rich.hobby;
import java.io.File;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
public class DataImporter {
public static void main(final String[] args) {
File baseDir = new File("C:\\workspaces\\hobby");
DataImporter importer = new DataImporter();
Configuration config = importer.setupDb(baseDir);
if (config != null) {
importer.importContents(new File(baseDir, "people.xml"), config);
}
}
private void importContents(final File file, final Configuration config) {
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Session dom4jSession = session.getSession(EntityMode.DOM4J);
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(file);
List list = document.selectNodes("//Person");
Iterator iter = list.iterator();
while (iter.hasNext()) {
Object personObj = iter.next();
dom4jSession.save(Person.class.getName(), personObj);
}
session.flush();
tx.commit();
session.close();
} catch (HibernateException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
}
private Configuration setupDb(final File baseDir) throws HibernateException {
Configuration cfg = new Configuration();
cfg.addFile(new File(baseDir, "name/seller/rich/hobby/Person.hbm.xml"));
cfg.addFile(new File(baseDir, "name/seller/rich/hobby/Hobby.hbm.xml"));
SchemaExport export = new SchemaExport(cfg);
export.setOutputFile("hobbyDB.txt");
export.execute(false, true, false, false);
return cfg;
}
}
This is the resulting content in the PEOPLE table.
PEOPLE_ID |NAME |HOBBIES
-------------------------------------------------------
1 |Dave |drinking, walking
2 |Geoff |football, ballet
3 |Anne |walking, karate
4 |Frank |karate, cross-stitch
...and these are the empty HOBBIES and PEOPLE_HOBBIES tables:
HOBBIES:
HOBBY
----------------------
0 rows selected
PEOPLE_HOBBIES:
PEOPLE_ID |HOBBY
---------------------------------------
0 rows selected
You might consider pre-processing your xml into something more suitable. It is generally better to represent lists of things as elements rather than comma separated attribute values.
For example:
<People>
<Person Id="1" Name="Dave" Hobbies="drinking, walking"/>
<Person Id="2" Name="Geoff" Hobbies="football, ballet"/>
<Person Id="3" Name="Anne" Hobbies="walking, karate"/>
<Person Id="4" Name="Frank" Hobbies="karate, cross-stitch"/>
</People>
Would be better as:
<People>
<Person Id="1" Name="Dave">
<Hobbies>
<Hobby>drinking</Hobby>
<Hobby>walking</Hobby>
</Hobbies>
</Person>
...
</People>
You could do this with an XSLT script - see XSLT - Best way to split and render comma separated text as HTML for an example.
That should then make it easier to import into Hibernate in the manner you desire.
When Hibernate reads the hobbies attribute, it just stores it as text directly into the Person table. It has no way of knowing about hobbiesSet at this point, since the only time that you are populating the set is when the object is read back out of the database again. But since the set was never populated in the database, it doesn't work.
The way you've configured both hobbies and hobbiesSet is confusing, and I wouldn't recommend mixing hobbies and hobbiesSet like that. I strongly suggest you read the XML into the object model yourself, including splitting the hobbies string, and then persist the manually-constructed objects to Hibernate in the normal way, using a hobbies collection.
I have found a partial solution, and thought it would be worth recording it here.
Unfortunately if there are duplicate keys in the list-attribute, you need to do a merge rather than a save on the element, and this is not yet supported for EntityMode.DOM4J. This is the comment from org.hibernate.type.CollectionType.replaceElements():
// TODO: does not work for EntityMode.DOM4J yet!
You can add an ElementHandler to the SAXReader to process each element and dynamically turn the attributes into child elements, this is my implementation:
SAXReader saxReader = new SAXReader();
saxReader.addHandler("/People/Person", new ElementHandler() {
public void onEnd(final ElementPath elementPath) {
Element element = elementPath.getCurrent();
Attribute hobbyAttribute = element.attribute("Hobbies");
if (hobbyAttribute != null) {
String hobbies = hobbyAttribute.getValue();
Element hobbiesList = new DefaultElement("Hobbies");
element.add(hobbiesList);
String[] hobbiesArray = hobbies.split(",");
for (String hobby : hobbiesArray) {
if (hobby.trim().length() > 0) {
Element hobbyElement = new DefaultElement("Hobby");
hobbiesList.add(hobbyElement);
Element idElement = new DefaultElement("id");
hobbyElement.add(idElement);
idElement.setText(hobby.trim());
}
}
}
}
public void onStart(final ElementPath elementPath) {
//no-op;
}
});
And the later loop is modified as follows:
while (iter.hasNext()) {
Object peopleObj = iter.next();
dom4jSession.merge(Person.class.getName(), peopleObj);
}
Once I'd updated the mapping files to handle the child elements and renamed the relevant methods in the domain objects it persists the related data (as long as there are no duplicates in the hobbies natch).
Updated Hobby.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">
<hibernate-mapping package="name.seller.rich.hobby">
<class name="Hobby" node="Hobby" table="HOBBIES">
<!--id name="id" column="HOBBY_ID">
<generator class="native"/>
</id-->
<id name="id" column="HOBBY_ID" type="string"/>
<set name="people" table="PEOPLE_HOBBIES" inverse="true">
<key column="HOBBY_ID"/>
<many-to-many column="PEOPLE_ID" class="Person"/>
</set>
</class>
</hibernate-mapping>
Updated Person.hbm.xml:
<hibernate-mapping package="name.seller.rich.hobby">
<class name="Person" node="Person" table="PEOPLE">
<id name="id" node="#Id" column="PEOPLE_ID"/>
<property name="name" node="#Name" column="NAME" type="string"/>
<!-- property name="hobbies" node="#Hobbies" column="HOBBIES" type="string"/-->
<set name="hobbies" node="Hobbies" table="PEOPLE_HOBBIES" cascade="save-update,persist">
<key column="PEOPLE_ID"/>
<many-to-many column="HOBBY_ID" class="Hobby"/>
</set>
</class>
</hibernate-mapping>
We tried using Hibernate's DOM4J and POJO entity modes in the same application a while ago. Maybe it's matured more by now, but we had nothing but problems with the DOM4J entity mode.
I'd recommend using Hibernate with your POJOs and use something like XStream or raw DOM4J for doing your XML serialization to and from the POJOs.

Categories

Resources