Hibernate, mapping inherited fields - java

Lets say I have a following class hierarchy:
class Person {
String name;
int age;
}
class DatabasePerson extends Person {
int databaseId;
}
Now I would like to map the DatabasePerson, please notice that I don't would like to tell hibernate that there exist a Person class, hibernate should only know about the DatabasePerson class while xml or annotation mapping. Is it possible to map the age and name fields with hibernate adopting the above mentioned condition ? In other words I would like to map the DatabasePerson class with hibernate and hibernate should not know about the Person class. In xml I would make it like this (Pseudo-code):
<hibernate class="DatabasePerson">
<field name="id"/>
<field name="name"/>
<field name="age"/>
</hibernate>
The reason for doing that is to keep the single responsibility principle. I don't would like to put the databaseId field into the Person class, because the person class shouldn't know that it is persisted. I don't would like to include any hibernate annotations into Person class because I have there pure entity logic and I don't want do import there any database related stuff like hibernate. All mappings I would like to do in the DatabasePerson class, this is the place where I would like to put the additional databaseId field, write hibernate annotations (or maybe xml, I don't know it right now, I would like to postpone this decision). I other words we want to keep the database related stuff and our application logic in separate classes.
Edit:
Can I use something like this?:
<class name="Person" table="PERSON" discriminator-value="P">
<discriminator column="DISCRIMINATOR" type="string" />
<property name="name" />
<property name="age" />
<subclass name="DatabasePerson" extends="Person" >
<id name="databaseId" column="ID">
<generator class="native" />
</id>
</subclass>
</class>
Notice that the database id is in the DatabasePerson scope in this xml.
Edit:
Is this xml mapping respective to the annotation 'mapped superclass' ? I think I preffer to use xml instead of annotations so my question is how to use #MappedSuperclass in xml:
<class name="Person" table="PERSON" discriminator-value="P">
<id name="databaseId" column="PERSON_ID">
<generator class="native" />
</id>
<discriminator column="DISCRIMINATOR" type="string" />
<subclass name="DatabasePerson" extends="Person" discriminator-value="E">
<property name="name" column="name" />
<property name="age" type="int" column="age" />
</subclass>
</class>

I think can you use MappedSupperclass annotation or singel table inheritance strategy for use only one table in database. Both case the JPA/Hibernate know about Person class, but It doesn't create table for this class. (Of course could you use XML adjustment instead of annotations, but I've used only annotations.)
#MappedSuperclass annotation.
If you want to use only one database table (DatabasePerson) which included all columns from both java classes, than you could use following annotations:
#MappedSuperclass
class Person {
String name;
int age;
}
#Entity
class DatabasePerson extends Person {
int databaseId;
}
I think in this case is better change Person class to abstract class.
InheritanceType.SINGLE_TABLE
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
name="yourtype",
discriminatorType=DiscriminatorType.STRING
)
class DatabasePerson{
int databaseId;
}
#Entity
#DiscriminatorValue("P")
class Person extends DatabasePerson{
String name;
int age;
}
This startegy use discriminator column and persist only one table.

Try it
<hibernate-mapping package="Yourpackage">
<class name="Person" table="PERSON" >
<id name="databaseId" column="ID">
<generator class="native" />
</id>
<discriminator column="DISCRIMINATOR" type="string" />
<property name="name" />
<property name="age" />
<subclass name="DatabasePerson" extends="Person" >
<property name="databaseId" column="database_Id" />
</subclass>
</class>
</hibernate-mapping>
May be possible same example here
http://viralpatel.net/blogs/hibernate-inheritence-table-per-hierarchy-mapping/

Hibernate XML configuration deals perfectly with it and I've used it extensively in one of the projects I worked on. My scenario was the following, which might sound different but it is just the same as yours.
class Entity {
private int _id;
public int getId() { return _id; }
public void setId(int id) { _id = id; }
}
class User extends Entity {
private String _email;
public String getEmail() { return _email; }
public void setEmail(String email) { _email = email; }
}
My intention here was to have User (and all my others entities) to inherit the id, as you're trying to have DatabasePerson inheriting Person.name and age.
Then, when configuring the mapping for User I've written the following XML file.
<hibernate-mapping>
<class name="User" table="User">
<id name="id" type="int" column="id">
<generator class="identity"/>
</id>
<property name="email" type="string">
<column name="email" sql-type="varchar(255)"/>
</property>
.....
</class>
</hibernate-mapping>
As you can see, there is no any single reference to Entity from the above XML file, which in your case translates to: your XML file only deals with DatabasePerson as Person wouldn't exist at all; Hibernate will use Java introspection to retrieve id from Entity, as Person.name for your case.
The only thing I'm not sure, is whether your Person.name and Person.age field can be left as protected or should be promoted to public. Either way, you can leave them as protected and (eventually) provide the method DatabasePerson.getName() and DatabasePerson.setName() (no need to provide these getter/setter at Person level).
As a side note, in other answers you have been suggested to use hibernate "subclass" or "MappedSuperclass". These are not required, and they usage is even semantically wrong in your case: They are supposed to be used when you have a DB entity extending another DB entity, but in your case DatabasePerson is a DB Entity extending a POJO class (Person).

Related

Hibernate: Several tables of one entity -> inherit hibernate-mapping

I have an entity from which i want to have several tables in my sql database.
For Example, I have the Java Class in which i have an collection of another Java Class
#Entity
class SqlEntity{
#Id
#Column(unique = true)
private Date date = null;
#Column
#OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL, CascadeType.PERSIST,
CascadeType.MERGE }, mappedBy = "CollectionData")
private Collection<CollectionData> collectionData = new ArrayList<>();
#Column(name="columNameX")
int attributeX;
#Column(name="columNamey")
int attributeY;
...
}
And i want different Data in different Tables according to where the Data are from:
SQL_ENTITY_GERMANY,
SQL_ENTITY_USA,
SQL_ENTITY_UK,
...
I was able to accomplish this by writing an xml-mapping for every table(before that I had only java annotations to map the entity).
But I had to write an complete mapping for every table like this:
<hibernate-mapping>
<class name="...SqlEntity" table="SQL_ENTITY_GERMANY"
entity-name="SQL_ENTITY_GERMANY">
<id name="date" type="date" column="date">
</id>
<property name="columnNameX" column="attributeX" type="int" />
<property name="columnNameY" column="attributeY" type="int" />
...
<bag name="collectionData" cascade="all">
<key column="date" />
<one-to-many class="COLLECTION_DATA_GERMANY" />
</bag>
</class>
<class name="...collectionData" table="COLLECTION_DATA_GERMANY"
entity-name="COLLECTION_DATA_GERMANY">
<id name="id" type="long" column="id">
<generator class="native" />
</id>
<property name="columnNameX" column="attributeX" type="int" />
<property name="columnNameY" column="attributeY" type="int" />
...
<many-to-one name="collectionData" class="SQL_ENTITY_GERMANY"
fetch="select">
<column name="date" not-null="true" />
</many-to-one>
</class>
</hibernate-mapping>
So if I want to change anything in one entity class (like adding members), i have to change it in every single xml-mapping, too.
So i thought it maybe possible to just inheritate the mapping, so that the annotations of the class are still guilty as long as i dont overwrite it.
I tried to google and search on stackoverflow on this topic, but i found only things about java classes inheritance.
Or is there another solution which could help me with this?
Since your database is non-normalized, the only way of achieving what you want is creating an abstract class and several concrete subclasses with appropriate country codes.
If you are able to alter a schema, I'd suggest adding a column COUNTRY_CODE VARCHAR(2) NULL to your SQL_ENTITY table, insert all data from your SQL_ENTITY_* tables.
You can do so by:
1)Creating a new SQL_ENTITY table
CREATE TABLE NEW_SQL_ENTITY (
id_entity INTEGER NOT NULL AUTO_INCREMENT;
value1 VARCHAR(100) NOT NULL;
value2 VARCHAR(100) NOT NULL;
country_code VARCHAR(2) NULL;
)
2) Inserting the data from the other tables:
INSERT INTO NEW_SQL_ENTITY (value1,value2,country_code) VALUES
(SELECT value1,value2, 'UK' from SQL_ENTITY_UK)
3) Repeating for all SQL_ENTITY tables

How to insert constant value in a join table with Hibernate in a many-to-many relation?

I have a mapping like A - AB - B but the AB table is also a join table for other tables (yeah, thanks, no comment :p). In the AB table, I have a CODE (non-null) column.
I can fetch the correct datas by adding a where tag in the mapping, but when I insert values, Hibernate does not add the value in the CODE column... Well, I don't know how to tell Hibernate to do that.
Here is the mapping of the A table to get a Set:
<set name="b" table="AB" lazy="false" where="CODE='1.2.3'">
<key column="A_ID" />
<many-to-many column="B_ID" class="B">
</many-to-many>
</set>
Hibernate create an insert with only the A_ID and the B_ID values, I'd like to tell Hibernate to insert CODE='1.2.3' in its SQL INSERT query.
Many thanks for your help,
UPDATE
The idea in a Java point of view is to have a signature like this in A: getB():Set<B> I do not want getAB():Set<AB>.
Thanks
for AB use two classes one for IDs ABIdClass(A_ID,B_ID) and the other for Code(and all extra columns) ABClass(CODE) and use default attribute of column for constant value:
<class name="ABClass" table="AB">
<composite-id name="id" class="ABIdClass">
<key-many-to-one name="a" class="A" column="a_id" />
<key-many-to-one name="b" class="B" column="b_id" />
</composite-id>
<property name="code" type="string">
<column name="code" not-null="false" default="1.2.3" />
<property/>
</class>
you can also try something like this:
ABIdClass abId = new ABIdClass();
abId.setA(a);
abId.setB(b);
ABClass ab = new ABClass();
ab.setId(abId);
ab.setCode("1.2.3");
a.getABClass().add(ab);
hope these be useful.
You should be able to achieve what you are looking for using the Hibernate Table per Class Hierarchy approach.
abstract class Mapping { private String code; }
class TableA {}
class TableB {}
class TableAB extends Mapping { private TableA a; private TableB b; }
class TableC {}
class TableD {}
class TableCD extends Mapping { private TableC c; private TableD d; }
class TableE {}
class TableF {}
class TableEF extends Mapping { private TableE e; private TableF f; }
<class name="TableA" table="tableA"/>
<class name="TableB" table="tableB"/>
<class name="TableC" table="tableC"/>
<class name="TableD" table="tableD"/>
<class name="TableE" table="tableE"/>
<class name="TableF" table="tableF"/>
<class name="Mapping" table="mapping">
<discriminator column="CODE" type="string"/>
<subclass name="TableAB" discriminator-value="1.2.3">
<property name="a" column="a_id"/>
<property name="b" column="b_id"/>
</subclass>
<subclass name="TableCD" discriminator-value="4.5.6">
<property name="c" column="a_id"/>
<property name="d" column="b_id"/>
</subclass>
<subclass name="TableEF" discriminator-value="7.8.9">
<property name="e" column="a_id"/>
<property name="f" column="b_id"/>
</subclass>
</class>
Note that the columns a_id and b_id are common for all relationships. This will ensure that a single mapping table can be used for different relationships as you want.

How to migrate same class with 2 entity-names to Spring Data JPA?

I am migrating an app to Spring Data JPA from Hibernate. I already migrated a few repositories and have that working. I now have a special case I need to convert.
I have this in my .hbm.xml:
<class name="SoundNotification" table="SoundNotification" entity-name="SoundNotificationWithData">
<id name="m_id" type="int" column="id" unsaved-value="-1">
<generator class="native"/>
</id>
<property name="m_name" column="name" unique="true" not-null="true"/>
<property name="m_data" column="data"
type="com.traficon.tmsng.server.common.service.persistence.impl.hibernate.usertype.BlobUserType"
not-null="true"/>
<property name="m_size" formula="OCTET_LENGTH(data)"/>
<property name="m_inUse"
formula="(select count(1) from EventTypeConfiguration etc where etc.soundNotification=id)"/>
</class>
<class name="SoundNotification" table="SoundNotification" entity-name="SoundNotificationWithoutData">
<id name="m_id" type="int" column="id" unsaved-value="-1">
<generator class="native"/>
</id>
<property name="m_name" column="name" unique="true" not-null="true"/>
<property name="m_size" formula="OCTET_LENGTH(data)"/>
<property name="m_inUse"
formula="(select count(1) from EventTypeConfiguration etc where etc.soundNotification=id)"/>
</class>
Notice how I only have 1 class SoundNotification, but it is used with 2 different entity-names (SoundNotificationWithData and SoundNotificationWithoutData)
Is it possible to convert this to Spring Data JPA? Would I need to create 2 java classes as a "workaround" ?
Another example which we have is this one:
<class name="FlowDataMessageImpl" entity-name="FlowDataPer10s" table="FlowDataPer10s">
...
</class>
<class name="FlowDataMessageImpl" entity-name="FlowDataPer20s" table="FlowDataPer20s">
....
</class>
<class name="FlowDataMessageImpl" entity-name="FlowDataPer2m" table="FlowDataPer2m">
...
</class>
Here we store the same "Java object" in different tables after we did some roll-up calculations. I would like to map this using JPA (or somebody to tell me it is a bad idea and I should use Hibernate directly like before for this)
To your first question: you will have to create two Java classes SoundNotificationWithoutData and SoundNotificationWithData, both classes extending the same third Java class, using the #Inheritance(strategy=InheritanceType.SINGLE_TABLE) and mapped with the #Table(name="SoundNotification") annotation. Also notice that you will not be able in plain JPA to create a property with a formula (property m_inUse), so you will have to use Hibernate-specific stuff OR load that property only when you need it.
To your second problem: again, either use Hibernate-specific stuff, OR use the #MappedSuperclass annotation on the superclass (which is extended by every FlowDataPer* classes), without using on it the #Entity and #Inheritance annotations. Of course you could also use the same solution as for your first question: different classes (FlowDataPer10s, FlowDataPer20s, ..) extending a base entity class, anntoated with #Entity and #Inheritance(strategy=InheritanceType.TABLE_PER_CLASS), but I find it more elegant with the #MappedSuperclass annotation.

Hibernate mapping with Generics

I have this class
public class FolderOwner<T> {
private T owner;
}
where logically, T could be a User
public class User {
private String id;
private String lastName, firstName, middleName;
}
or a Department
public class Department {
private long id;
private String name;
}
Now, I have this class which has an attribute of an instance of FolderOwner
public class Folder {
private FolderOwner owner;
//other attributes
}
My question is how to map them in hibernate, considering those generic types in FolderOwner? I already read some answers on this forum, but they've done them using annotations. I'm quite new with Hibernate mapping and I prefer XML mapping than annotation momentarily.
My mapping for User
<class name="com.fileManagement.dataDesign.User" table="user">
<id name="id" type="string" column="id"/>
<property name="lastName" column="lastName" type="string" not-null="true"/>
<property name="firstName" column="firstName" type="string"/>
<property name="middleName" column="middleName" type="string"/>
</class>
My mapping for Department
<class name="com.fileManagement.dataDesign.Department" table="department">
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="name" column="name" type="string"/>
</class>
Please help. Thanks.
If you want basic data fields, which are present in all tables use the #MappedSuperclass in you base class and extend the entity from there.
Update
There are different types of inheritance you can use with hibernate. See the excellent documentation at http://docs.jboss.org/hibernate/orm/3.5/reference/en/html/inheritance.html.
Apart from the inheritance of hibernate, there is also jpa on top with the #mappedsuperclass annotation. The advantage is that you super class does nit have to be an entity at all, and you can reuse everything you already annotated.
My use case were three identical tables (dimension1, dimension2, dimension3) that where referenced by another one.
But you can think of many other good use cases like creation, deletion and update timestamps, onwer, createdBy and updatedBy fields...
All you have to do is to annotate the super class fields as you would normally do in you entity and extend the class.

Can Hibernate Tools reverse-engineer DB entities with annotations instead of cfg.xml files?

I have a number of database tables I need to reverse-engineer POJOs for. I've gotten as far as getting the bare POJOs created as well as the cfg.xml files:
Simple POJO:
public class AddressType implements java.io.Serializable {
private long addressId;
private char addressType;
private String addressDescription;
}
Simple cfg.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>
<class name="com.mycompany.model.Addytyp" table="ADDYTYP" schema="XX" catalog="BANANA">
<comment>Address Types</comment>
<id name="addressType" type="char">
<column name="ADDRESS_TYPE" length="1" />
<generator class="assigned" />
</id>
<property name="addressId" type="long">
<column name="ADDRESS_ID" precision="10" scale="0" not-null="true">
<comment>Address ID</comment>
</column>
</property>
<property name="addressDescription" type="string">
<column name="ADDRESS_DESCRIPTION" length="25" not-null="true">
<comment>Address Decription</comment>
</column>
</property>
</class>
</hibernate-mapping>
What I was wondering is, is there a setting or a tool that will reverse-engineer the POJOs with the column and ID information as annotations in the POJO instead of in a separate file? For example:
Simple POJO with Annotations:
#Table(name="ADDYTYP")
public class AddressType implements java.io.Serializable {
#Id
#Column( name="ADDRESS_ID", precision=10, scale=0, nullable=true)
private long addressId;
#Column(name="ADDRESS_TYPE", length=1)
private char addressType;
#Column( name="ADDRESS_DESCRIPTION", length=25 nullable=true)
private String addresDescription;
}
Anyone know of a setting or a tool that will do this?
Duh. This one is so simple I'm embarrassed I asked it. This is a simple matter of configuration of the Hibernate Tools task. The relevant documentation is here.
The Ant POJO generation task, properly configured to generate annotations, looks like this:
<hbm2java jdk5="true" ejb3="true" />
These options default to "false" for some reason. From the Hibernate docs:
jdk Code will contain JDK 5 constructs
such as generics and static imports (Default = False)
ejb3 Code will contain EJB 3 features, e.g. using annotations
from javax.persistence and org.hibernate.annotations (Default = False)
Hope this helps!

Categories