Hibernate inserts child into parent table - java

I have the followings at Java level:
#Entity
#Table(name = "WORD_DOC")
public class WordDoc extends Doc {}
#Entity
public class Doc extends BaseDoc {}
#Entity
public abstract class BaseDoc {}
And the following tables in the DB:
WORD_DOC
id int8
version int8
...
BASEDOC
id int8
...
When I try to save a WordDoc, the Hibernate fails with the following error:
STATEMENT: insert into BaseDoc (..., ..., .....) values (..., ..., .....)
ERROR: column "version" of relation "basedoc" does not exist at character 40
Why it tries to persist the WordDoc into the parent class's table? It has several additional attributes so it not fit into that.

Hibernate supports four inheritance mapping strategies. If you don't specify any (like in your example), the SINGLE_TABLE strategy is used. This means all your entities end up in the BaseDoc table (which is what you observed).
See this article to see how to use a different mapping strategy: Inheritance Strategies with JPA and Hibernate – The Complete Guide.
In your case, you might need #MappedSuperclass or #Inheritance(strategy = InheritanceType.JOINED). The first maps every concrete (non-abstract) class to its own table that includes all the information of its superclass(es). The second maps all classes, including the abstract ones, to their own tables, but requires joins.

Related

How to extend #Entity from commons lib?

I would like to extend an #Entity from a library that I don't have control of:
//commons package that I don't have control of
#Entity(name = TABLE_NAME)
public class CommonEntity {
public static string TABLE_NAME = "common_entity";
//like 30 fields with #Column annotations, hibernate validations etc
}
But how? I tried as follows, which fails:
//it's on a different database, just the schema should be extended
#Entity(name = CommonEntity.TABLE_NAME)
public class CustomEntity extends CommonEntity {
//some more fields
}
Result:
Caused by: org.hibernate.tool.schema.spi.SchemaManagementException:
Schema-validation: missing column [dtype] in table
As a result, I only want to have a single common_entity table extended with my custom fields. I don't plan to store CommonEntity objects directly to the table.
Is that possible? Would I somehow have to create that magical dtype column myself?
At the end I simply let hibernate create the dtype column, so I can at least inherit all configuration from the commons entity, while only drawback is to have one "unneccessary" column.

JPA Entity for View containig joins and aliases

I have the following query using which I want to create an entity class for the columns which I am retrieving from the query.
select e.emp_id,
c.company_code,
mg.emp_id as mangaer_code
from employee e
left join company c on e.emp_id = c.emp_id
left join manager mg on e.emp_id = c.emp_id = mg.emp_id
How to create an entity class from these columns and what variables are needed to take it in entity class to refer to these columns?
View is a virtual table based on the result-set of an SQL statement or a function and JPA treats it as a regular table. Create a entity and use JPA annotations as below
#Entity
#Immutable
#Table(name = "employee_view")
public class EmployeeView{
//define the required columns from view here
}
For more details, refer to this article that I found https://medium.com/#jonathan.turnock/exposing-subset-view-of-the-database-with-a-jpa-repository-over-rest-5b9d6e07344b

search though all tables extended from #MappedSuperclass

I have and issue with searching in entities that are extended from #MappedSuperclass. I created a class PhoneBook and extended 2 entities from it: FirstPhoneBook and SecondPhoneBook. The structure looks the following:
#MappedSuperclass
public abstract class PhoneBook {
...
#Entity
#Table(name = "first_phone_book")
public class FirstPhoneBook extends PhoneBook {
...
#Entity
#Table(name = "second_phone_book")
public class SecondPhoneBook extends PhoneBook {
...
These tables are absolutely similar. I discribe all fields in PhoneBook class, childs have only default constructor in it. External system sends a phone number as a parameter. Depending on whether tables contain such number or not my system responds with a word.
The question is: how can I search separately in each table that is extended from #MappedSuperclass without hardcoding each child class name?
I could only find variants of search by value like that:
currentSession.get(Employee.class, theId);
but there is explicit call to entity class. I want this to be extendable without need to write new DAO for each new entity added. Current method signature looks the following:
public <T extends PhoneBook> T findByNumber(String number);
What you describe is polymorphic queries, i.e. queries that reference the parent class. The Hibernate documentation says this is not well supported when using #MappedSuperclass inheritance:
Because the #MappedSuperclass inheritance model is not mirrored at the database level, it’s not possible to use polymorphic queries referencing the #MappedSuperclass when fetching persistent objects by their base class.
If polymorphic queries are frequently used, it's better to use the table per class inheritance strategy:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class PhoneBook {
...
#Entity
#Table(name = "first_phone_book")
public class FirstPhoneBook extends PhoneBook {
...
#Entity
#Table(name = "second_phone_book")
public class SecondPhoneBook extends PhoneBook {
...
You can then fetch an entity using the superclass:
PhoneBook phoneBook = currentSession.get(PhoneBook.class, theId);
and Hibernate would typically use a UNION to do the query with both tables.
This being said, even with #MapperSuperclass, Hibernate can still query all tables for classes that extend the parent class. You can use the following JPA query (note that it uses the fully qualified class name of the parent class):
Query<PhoneBook> query = currentSession.createQuery("from " + PhoneBook.class.getName() +
" where id = :id", PhoneBook.class);
query.setParameter("id", theId);
The difference is that here it's not querying an entity, but just all classes that extend a parent class. Also in this case, unlike with the table-per-class strategy, Hibernate will not use a UNION, but send a query to each table, in this case two separate SQL queries instead of one.

#GeneratedValue polymorphic abstract superclass over MySQL

In a Spring MVC application using Hibernate and MySQL, I have an abstract superclass BaseEntity that manages the values of the IDs for all the other entities in the model. The id field uses #GeneratedValue. I am encountering a problem whenever my code tries to save any of the subclasses that extend BaseEntity. The problem comes with the choice of GenerationType for the #GeneratedValue.
At every place in my code where a subclass of BaseEntity tries to save to the underlying MySQL database, I get the following error:
ERROR SqlExceptionHelper - Table 'docbd.hibernate_sequences' doesn't exist
I have read many postings about this on SO and on google, but they either deal with other databases (not MySQL) or they do not deal with abstract superclasses. I cannot solve the problem by using GenerationType.IDENTITY because I am using an abstract superclass to manage id fields for all entities in the model. Similarly, I cannot use GenerationType.SEQUENCE because MySQL does not support sequences.
So how do I solve this problem?
Here is the code for BaseEntity.java:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
protected Integer id;
public void setId(Integer id) {this.id = id;}
public Integer getId() {return id;}
public boolean isNew() {return (this.id == null);}
}
Here is an example of the code for one of the entities that extends BaseEntity:
#Entity
#Table(name = "ccd")
public class CCD extends BaseEntity{
//other stuff
}
Here is the DDL:
CREATE TABLE IF NOT EXISTS ccd(
id int(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
#other stuff
)engine=InnoDB;SHOW WARNINGS;
Here is the JPQL code in the DAO:
#Override
#Transactional
public void saveCCD(CCD ccd) {
if (ccd.getId() == null) {
System.out.println("[[[[[[[[[[[[ about to persist CCD ]]]]]]]]]]]]]]]]]]]]");
this.em.persist(ccd);
this.em.flush();
}
else {
System.out.println("]]]]]]]]]]]]]]]]]] about to merge CCD [[[[[[[[[[[[[[[[[[[[[");
this.em.merge(ccd);
this.em.flush();
}
}
EDIT:
The reason I cannot use #MappedSuperClass in this situation is that I need to have ManyToOne relationships that allow for multiple subtypes to be used interchangeably. Look at the AccessLog class below as an example. It has an actor_entity and a target_entity. There can be many types of actor entities and many types of target entities, but they all inherit from BaseEntity. This inheritance enables the underlying accesslogs data table in MySQL to just have one actor_entity_id field and just one target_entity_id field instead of having to have several fields for each. When I change #Entity above BaseEntity to #MappedSuperClass, a different error gets thrown indicating that AccessLog cannot find BaseEntity. BaseEntity needs #Entity annotation in order for AccessLog to have polymorphic properties.
#Entity
#Table(name = "accesslogs")
public class AccessLog extends BaseEntity{
#ManyToOne
#JoinColumn(name = "actorentity_id")
private BaseEntity actor_entity;
#ManyToOne
#JoinColumn(name = "targetentity_id")
private BaseEntity target_entity;
#Column(name="action_code")
private String action;
//getters, setters, & other stuff
}
SECOND EDIT:
As per JBNizet's suggestion, I created a hibernate_sequences table as follows:
CREATE TABLE IF NOT EXISTS hibernate_sequences(
sequence_next_hi_value int(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
)engine=InnoDB;SHOW WARNINGS;
But now I am getting the following error:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'sequence_name' in 'where clause'
Here is the hibernate sql causing the error, followed by the next 2 lines of the stack trace:
Hibernate: select sequence_next_hi_value from hibernate_sequences where sequence_name = 'BaseEntity' for update
ERROR MultipleHiLoPerTableGenerator - HHH000351: Could not read or init a hi value
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'sequence_name' in 'where clause'
How do I resolve this?
What a mess... AUTO_INCREMENT is MySQL's hidden sequence. The radical problem is that MySQL can not insert and return the PK at the same time, but Hibernate need this while INSERTing a new Entity.
The Problems you run into:
If Hibernate save a new Entity, he try to immerdentelly set the id to the new EntityBean. Therefore hibernate must read what ID will the Database use before hibernate save the new Tuple to the Table.
If you have multiple Servers who access the database, you shall let hibernate's session-factory decide to use the built-in sequence(AUTO-INCREMENT) or let hibernate decide (GenerationType.AUTO/GenerationType.IDENTITY) how large the open range of reserved PK's is (Job of a DB-Architect). (We have about 20 servers to one Database, so on a good-used table we use a PK-distance of +100). If only one server have access to the database GenerationType.TABLE shall be correct.
Hibernate must calculate the next id by yourself using max(*)+1 but:
What if two requests ask for max(*)+1 at the same time/with the same result? Right: The last try to insert will fail.
So you need to have a Table LAST_IDS in the database who stores the last Table-PK's. If you like to add one, you must do this steps:
Start read-optimistic transaction.
SELECT MAX(address_id) FROM LAST_IDS
store the maximum in a java-variable i.e.: $OldID.
$NewID = $OldID + 1. (+100 in pessimistic-lock)
UPDATE LAST_IDS SET address_id= $newID WHERE address_id= $oldID?
commit the read-optimistic transaction.
if commit was successfull, store $newID to setID() in the HibernateBean you like to save.
Finally let Hibernate call the insert.
This is the only way i know.
BTW: Hibernate-Entitys shall only use inheritance if the Database support inheritance between tables like PostgreSQL or Oracle.
Because you use the TABLE identifier generator you need to have that table created. If you are not using the enhanced identifier generators, chances are you are going to use the MultipleHiLoPerTableGenerator.
The MultipleHiLoPerTableGenerator can use one table for all table identifier generators.
My suggestion is to grab the table ddl from your integration tests, in case you use hbmddl to build the test schema. If you use flyway or liquibase for testing, you can add a maven plugin to generate the ddl schema.
Once you have the schema, you need to take the exact create table command and make add it to your MySQL database.

Correct database design for JPA entity inheritance

I'm starting a new project using JPA 2 + Hibernate 4.2.6 for data access.
I have two tables in my DB representing two different kinds of Answer, so I have answer_type_a and answer_type_b tables.
The are identical except for one field.
Now I'm creating my model classes and I'd like to inherit my AnswerA and AnswerB entities from a commom super-class or interface Answer.
I read some docs about entity inheritance:
http://docs.oracle.com/javaee/6/tutorial/doc/bnbqn.html
http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/inheritance.html#inheritance-tablepersubclass
but I have not a clear view on how to structure my DB and my entity classes to achieve this. Can you help me?
You need table Answer - that is super for particular others, where you should place all common columns for answer_type_a and answer_type_b. Assume Answer.a_id is primary key, then answer_type_a.a_id answer_type_b.a_id are simultaneously PRIMARY KEY and FOREIGN KEY.
Of course don't forget to place distinguished columns to answer_type_a and answer_type_b
+---------+
| Answer |
+---------+ ----------------------->--+--------------+
| a_id +------) | answer_type_b|
---->-+--------------+ +--------------+
... | answer_type_a| | a_id |
+--------------+ ...
| a_id |
....
You need to create one class named Answer with the common fields and you will annotate it with #MappedSuperclass. Additionally, you will have 2 entity classes, named AnswerA and AnswerB which will only hold the "extra" fields.
If you have 2 tables, then you need to use the TABLE_PER_CLASS inheritance type. So add #Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) to the Answer class, and probably a #Table annotation to AnswerA and AnswerB.
But if I were you, I would use one table for all answers, with all the needed columns + one discriminator column so you can distinguish if a row is answer type a or type b.
Well, as I understand your question is about mapping:
You need a java class Answer which is actually your super class. As there is no corresponding DB table for that, it must be annotated with #MappedSuperclass
#MappedSuperclass
public class Answer {
//fields&properties
}
And now the two classes come, which are entities, as they are persisted in DB tables:
#Entity
#Table("answer_type_a")
public class AnswerA extends Answer {
//here you add the field that is not common with AnswerB
}
#Entity
#Table("answer_type_b")
public class AnswerB extends Answer {
//here you add the field that is not common with AnswerA
}
PS: IMHO you don't need any inheritance annotations, as the mapped super class is not an entity.
I recently was in a similar situation like you. The best solution found was using an abstract class as a super type, and extending that class to the concrete types (which will represent your entity).
So for your situation you would model something like this.
#MappedSuperClass
public abstract class Answer{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Integer id;
#Column
String commonFieldOne;
#Column
String commonFieldTwo;
#Column
String commonFieldThree;
...
}
Note that I used the #MappedSuperClass annotation. This tells JPA that this is a super class that will not be instantiated and should not have a database table associated with it.
Also note that the Id which will serve as the primary key for both child tables is defined in this class
Now for you concrete classes (which will be mapped to tables in you database), you would use the #Entity annotation.
#Entity
#Table("answer_type_a")
public class AnswerA extends Answer{
//All common fields from Answer table will be included in the DB tables as columns automatically
#Column
String uniqueFieldOne;
....
}
You can do the same for #Table("answer_type_b")
Hope this helps!
Just create one table in your database e.g answer which contains both fields. You must have three classes Class 1 : Answer, Class 2: Answer_Type_A extends Answer, Class 3: Answer_Type_B extends Answer. There will be an hbm file for Answer (superclass) and two hbm for each Answer_Type_A and Answer_Type_B. In sybclass hbm files include:
<hibernate-mapping> <subclass name="com.test.Answer_Type_A"
extends="com.test.Answer">
<property name="fieldA" />
</subclass>
</hibernate-mapping>

Categories