Hibernate twice embedded entities - java

I have Hibernate 5.2.10 version and hibernate-jpa-2.1-api with version 1.0.0.Final. I am using MairaDB as database. In persistance.xml, set the property hibernate.ejb.naming_strategy as DefaultComponentSafeNamingStrategy but still I receive the same error:
Repeated column in mapping for entity. I do not want to use #attributeoverrides hibernate and I tried different methodes but still the same error. I want two or more embedded enities.
Thanks

You can't use DefaultComponentSafeNamingStrategy with Hibernate 5, because of it is an implementation of the old NamingStrategy interface from Hibernate 4.
As you probably know, Hibernate 5 uses two new interfaces ImplicitNamingStrategy and PhysicalNamingStrategy.
You can use this implicit naming strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl.
You will need to set hibernate.implicit_naming_strategy property (not hibernate.ejb.naming_strategy).
For these entities
#Embeddable
public class AuthorInfo {
#Column
private String authorInfo;
#OneToOne
private Book bestBook;
}
#Entity
public class Book {
#Id
private Long pid;
#Embedded
private AuthorInfo firstAuthor;
#Embedded
private AuthorInfo secondAuthor;
}
it creates this schema
create table Book (
pid bigint not null,
firstAuthor_authorInfo varchar(255),
secondAuthor_authorInfo varchar(255),
firstAuthor_bestBook_pid bigint,
secondAuthor_bestBook_pid bigint,
primary key (pid)
)
Unit test to check a schema: TwoEmbeddedStrategyTest.java

Related

Mapping recursive relation via secondary table with Spring Data

I have database such as this:
CREATE TABLE unit
(
id INTEGER NOT NULL,
name VARCHAR,
);
CREATE TABLE unit_composition
(
parent_id INTEGER NOT NULL,
child_id INTEGER NOT NULL,
quantity INTEGER,
CONSTRAINT child_fk FOREIGN KEY (parent_id)
REFERENCES public.refdse (id) MATCH SIMPLE,
CONSTRAINT parent_fk FOREIGN KEY (parent_id)
REFERENCES public.refdse (id) MATCH SIMPLE
);
ALTER TABLE unit_composition
ADD CONSTRAINT composit_pk PRIMARY KEY (parent_id, art_nr);
I have a table of manufactory units. Each unit can have multiple sub-units, and sub-units can have multiple sub-sub-units and so on. Also I have a quantity field that shows how many sub-units are needed to manufactor a single unit. So it is kind of a tree relation.
Now I want to map it to classes with Spring Data. I have a Unit class with an Id and Name:
#Entity
#Table(name = "unit")
class Unit {
#Id
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
...
}
I've created a secondary class Part:
class Part {
private Unit unit;
private int quantity;
...
}
And I need unit class to have a field like List subUnits.
I tried to do it with a #SecondaryTable and #JoinColumn annotations, but I got an error saying "Relation unit_unit does not exist".
Also I tried to make Part an #Entity but it has no Id field.
Alternatively I tried to make #Embeddable class PartId and insert an instance into Part class like this:
#Embeddable
public class PartId implements Serializable {
private Unit parentUnit;
private Unit unit;
I'm getting an error in PartId class saying that "Basic type should not be Persistence Entity" because it's Embeddable and don't have a table assign to it.
So how can I make this work being able to get recursivly all sub-units (with sub-sub-units and so on) of a given Unit? I don't quite get it how can I map an entity that is really just links from table to itself.
So my first solution was to make Jdbc template repository and simply build necessary lists manually via SQL-queries. But I found much better and simplier solution that required adding an id column to unit_composition table and therefore making Part class an #Entity with #ManyToOne relationship to a Unit class. And the rest was simply done by Spring Data.

Define an inherited column to be part of composite primary key in JPA

We have an abstract base entity class that defines columns found in all of our entities (e.g. creation and modifications timestamps and related user IDs). For most of our entities, there are two related database tables: one that holds the "live" data, i.e. the current state of the entity; and one that holds the audit trail, i.e. all historical versions of the entity, as snapshots.
One of the columns shared by all entities (and defined in the base entity class) is a version number column, which is a simple running sequence that tells how many edits have been made to that particular entity. The version column is present in both the audit table and the "regular" table, but in the audit table, the version column is also part of a composite primary key, which is formed by combining the primary key of the "regular" entity and the version number. Simplified example (some entity-level annotations omitted for brevity):
#MappedSuperclass
public abstract class BaseEntity {
#Column(name = "version")
private Long version;
// Timestamps, user IDs etc.
}
#MappedSuperclass
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class PersonBase extends BaseEntity {
// All basic columns of a person defined here
}
public class Person extends PersonBase {
#Id
#Column(name = "id")
private long id;
// All references to other entities defined here
}
public class PersonAudit extends PersonBase {
#Id
#Column(name = "id")
private long id;
// FIXME: Version column should be part of composite primary key
// All foreign key columns defined here (can't have
// a direct FK relationship in an audit table, as you
// don't know which version to link to)
}
Is it possible to make the inherited version column be part of a composite primary key in the audit entities? I tried declaring the version column also in the PersonAudit table and adding the #Id annotation to it, but after that the version field in BaseEntity wasn't populated anymore. It'd be very convenient to have the field in BaseEntity as we use it in some generic Criteria Queries using the JPA static metamodel, and if we move the field declaration to the individual audit entities, we can't reference the column anymore in those queries.
P.S. We're using Hibernate, so Hibernate-specific solutions are also acceptable if JPA doesn't enable this.

Hibernate 5.1.x naming Strategy (backward compatible with Hibernate 4.x)

I'm using Spring Boot 1.3.3.RELEASE. By default Spring Boot uses the Hibernate Version 4.x. I'm trying to use new Hibernate i.e 5.1.0 FINAL (as of now).
I'm using Gradle so to override the Hibernate Version I've added the following line
ext['hibernate.version']="5.1.0.Final"
followed the steps of SpringBoot 1.3.0 support hibernate 5?
I'm using following for naming Strategy
spring.jpa.properties.hibernate.naming.implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
spring.jpa.properties.hibernate.naming.physical_strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
I've have a Entity class
#Entity
public class AppUser {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Length(max = 100)
private String username;
#NotNull
#Length(max = 100)
private String firstName;
#NotNull
#Length(max = 100)
private String lastName;
#Length(max = 100)
private String middleName;
#NotNull
#Length(max=100)
private String email;
#NotNull
#Length(max = 100)
private String password;
#NotNull
private boolean enabled;
}
On Hibernate 4.x it executes the query
create table app_user (
id bigint not null auto_increment,
email varchar(100) not null,
enabled bit not null,
first_name varchar(100) not null,
last_name varchar(100) not null,
middle_name varchar(100),
password varchar(100) not null,
username varchar(100) not null,
primary key (id)
)
on 5.x it executed the query
create table AppUser (
id bigint not null auto_increment,
email varchar(100) not null,
enabled bit not null,
firstName varchar(100) not null,
lastName varchar(100) not null,
middleName varchar(100),
password varchar(100) not null,
username varchar(100) not null,
primary key (id)
)
How can I set the naming strategy such that Hibernate Uses 5.x underscore (as 4.x) on Table name and Column Name
Firstly, you don't need
org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
because of it does nothing and is used by Hibernate as default.
Hibernate 5 doesn't have a strategy that you want. All strategies are JPA compliant (generate names like AppUser). So you need to implement your own.
For an example a physical naming strategy
public class UnderscorePhysicalStartegy extends PhysicalNamingStrategyStandardImpl {
#Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
return context.getIdentifierHelper()
.toIdentifier(NamingStrategyUtils.classToName(name.getText()));
}
}
It uses NamingStrategyUtils.
Keep in mind, if you specify an explicit name
#Entity
#Table(name = "AppUser")
public class AppUser {
}
you will have anyway a table name app_user. If you don't want such behavior use an implicit naming strategy.
I did some research work on naming strategies. You can refer Hibernate5NamingStrategy, it generates table and column names with underscores like you need and constraint names (unique, foreign key) as well.
This class is used to generate names: HibernateNamingStrategy.
How to use Hibernate5NamingStrategy
The naming strategy can be configured using StrategyOptions.
For example, to use strategy without the prefixes (like f_):
StrategyOptions options = StrategyOptions.builder().withoutPrefixes().build();
Hibernate5NamingStrategy strategy = new Hibernate5NamingStrategy(options);
Other examples: Hibernate 5 Implicit Naming Strategy
Except that, ImprovedNamingStrategy for Hibernate 5 can be used to simulate the behaviour of Hibernate 4 ImprovedNamingStrategy.
I am providing my analysis for anyone to use:
If you are providing #Table and #Column annotation in your entity classes with names provided with an underscore i.e. user_id i.e. #Column(name="user_id"), it will take the column name as user_id; if you give it as userid then it will change to user_id if you use no strategy or implicit strategy (specifically spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl). So, if you want a strategy where the entity attribute name changes to one with underscore and lowercase letters i.e. something from userId to user_id, you should use implicit or no strategy (which actually uses implicit strategy).
If you don't want your naming strategy to add an underscore to the column name or class name, then the strategy that you need to use would look like: spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl. The things that you provide in annotations #Table and #Column’s name attribute would remain as it is.
If you don't want to provide annotations and want to manually handle the table name and column names, you should extend the class org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl and override the required methods. If you still use annotations for some of the cases here, remember the overridden methods will apply on the names written in those annotations. spring.jpa.hibernate.naming.physical-strategy=example.CustomStrategy

JPA 2: how to declare primary-key columns in #ElementCollection tables

in JPA2 when we are using Embed-able (Basic Type like String.. etc ) object in Entity using with #ElementCollection and #CollectionTable annotation , the new table is created , but in new table how to declare primary-key contraint in column ? following is my code
public class Employee {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
private String salary;
#Transient
private String phnNum;
#Enumerated(EnumType.STRING)
private EmployeeType type;
#ElementCollection
#CollectionTable(name="vacations" , joinColumns=#JoinColumn(name="Emp_Id"))
private Collection<Vacation> vacationBooking;
#ElementCollection
private Set<String> nickNames;
...................
with this code the "vacation" and "employee_nickname" two tables are created in schema. but i want to declare the one primary-key column in both table . what i do for this?
It looks like a primary key per se is not supported by JPA 2.0:
From Wikibooks:
The JPA 2.0 specification does not provide a way to define the Id in the Embeddable. However, to delete or update an element of the ElementCollection mapping, some unique key is normally required. Otherwise, on every update the JPA provider would need to delete everything from the CollectionTable for the Entity, and then insert the values back. So, the JPA provider will most likely assume that the combination of all of the fields in the Embeddable are unique, in combination with the foreign key (JoinColumn(s)). This however could be inefficient, or just not feasible if the Embeddable is big, or complex.
Some JPA providers may allow the Id to be specified in the Embeddable, to resolve this issue. Note in this case the Id only needs to be unique for the collection, not the table, as the foreign key is included. Some may also allow the unique option on the CollectionTable to be used for this. Otherwise, if your Embeddable is complex, you may consider making it an Entity and use a OneToMany instead.
Do you mean that you want to assign 'id' from Employee table as foreign key to the Vacation table?
In that case, you should use #OneToMany instead of #ElementCollection

Why does not jpa2/eclipselink generate on delete cascade SQL?

#Entity
public class MUser implements Serializable, MemoEntity {
private static final long serialVersionUID = 1L;
#Id
private String email;
#OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)
private Set<Meaning> mengs = new HashSet<Meaning>();
Shouldn't this mean that I get the constraint with a "on delete cascade"?
This is what gets generated instead:
CREATE TABLE MUSER_MEANING (MUser_EMAIL VARCHAR(255) NOT NULL, mengs_OBJID INTEGER NOT NULL, PRIMARY KEY (MUser_EMAIL, mengs_OBJID))
CREATE TABLE MUSER_MEANING (MUser_EMAIL VARCHAR(255) NOT NULL, mengs_OBJID INTEGER NOT NULL, PRIMARY KEY (MUser_EMAIL, mengs_OBJID))
ALTER TABLE MEANING ADD CONSTRAINT MEANING_USR_EMAIL FOREIGN KEY (USR_EMAIL) REFERENCES MUSER (EMAIL)
ALTER TABLE MUSER_MEANING ADD CONSTRAINT MSRMEANINGMsrEMAIL FOREIGN KEY (MUser_EMAIL) REFERENCES MUSER (EMAIL)
I'm trying to make it as such that deleting a MUser deletes all Meanings associated to it.
Why does not jpa2/eclipselink generate on delete cascade SQL?
Because that's not how things work. Specifying a cascade=REMOVE means that the remove operation will be cascaded to entities by the JPA provider, not at the database level using a constraint.
Some providers do have extensions to use a cascade delete constraint at the database level instead of the regular mechanism. For example with Hibernate, you can specify:
#OnDelete(action=OnDeleteAction.CASCADE) on joined subclasses: use a SQL cascade delete on deletion instead of the regular Hibernate mechanism.
But I don't know if EclipseLink has something equivalent.
References
JPA 2.0 Specification
Section 3.2.3 "Removal"
Hibernate Annotations Reference Guide
2.4. Hibernate Annotation Extensions

Categories

Resources