Hibernate view mapping - java

I got two classes. A class named person and a class named group. Now I created a view linking these classes and created a class with an #Immutual annotation.
View result
| person_id | group_id |
| ----------|----------|
| 1 | 2 |
| 2 | 2 |
| 3 | 4 |
| ... | ... |
Class PersonGroup
#Entity
#Table(name = "person_group")
#Immutable
public class PersonGroup {
#Id
#Column
private Person person;
#Column
private Group group;
public Person getPerson() {
return this.person;
}
public Group getGroup() {
return this.group;
}
}
Now i want to map PersonGroup to Person and Group. Like this:
Class Person
#Entity
public class Person {
...
private PersonGroup group;
...
}
Class Group
#Entity
public class Group {
...
private Set<PersonGroup> person;
...
}
Is that possible? If yes, which annotations should I use? I tried a lot and nothing worked for me.
Regards,
Xy

If you want to use PersonGroup in the Person Model then u have to use #Embeddable annotations to embed a value type object into our Entity class.
Like This :-
#Entity
#Embeddable
#Table(name = "person_group")
#Immutable
public class PersonGroup {
.....
then add anotation #Embedded to the Person Class.
Like this :-
#Entity
public class Person {
...
#Embedded
private PersonGroup group;
If you want to use the PersonGroup meodel in GroupModel Then use the #ElementCollection Annotationin Group Class as Below
#Entity
public class Person {
...
#ElementCollection
private Set<PersonGroup> person;
Please refer to the below tutorial.
Doc1 and Doc2

Related

JPA #OneToOne Mapping a relationship with ReadOnly Oracle data without primary key

Preamble An Oracle DB read-only(I don't have access) has the following two tables:
person
person table
| id | name | gender |
| -- | ------ | ------ |
| 2001 | Moses | M |
| 2002 | Luke | M |
| 2003 | Maryam | F |
PK(id)
reference
reference table
| sep | guid | table_name |
| --- | -------- | ---------- |
| 2001 | EA48-... | person |
| 2002 | 047F-... | person |
| 2003 | B23F-... | person |
| 2003 | 3E3H-... | address |
| 2001 | H2E0-... | address |
| 2001 | 92E4-... | report |
No PK, it is generated by some triggers
The person table is a straight forward table with a primary key. The reference table are generated via a trigger that stores the id(PK) in sep column of any table and the table name that is store in table_name column (Note: Since no primary key, the reference table stores duplicate values in the sep column but distinct value into guid.)
Requirement
I need to use JPA to get the record from the reference table and map to the person record (person.id and other table.id are stored in reference.sep column) using Jackson as follows
{
"id": 2001,
"name": "Moses",
"gender": "M",
"reference": {
"sep": 2001,
"guid": "EA48-...",
"tableName": "person"
}
}
Entity (Person)
#Entity
#Table(name="person")
public class Person implements Serializable {
#Id
private Long id;
private String name;
private String gender;
#OneToOne
#JoinColumn(name = "id", referencedColumnName = "sep", insertable = false, updatable = false)
private Reference reference;
// Getters & Setters
}
Entity (Reference)
#Entity
#Table(name="reference")
public class Reference implements Serializable {
private Long sep;
private String guid;
private String tableName;
//Getters & Setters
}
Problem 1
JPA throws error of no #Id annotation on Reference table.
Problem 2
If I add the #Id annotation on the sep field, JPA throws error of duplicate values for that column.
Problem 3
If I add the #Id annotation on the guid field (it is unique field), JPA throws error of mapping a Long to a String field (org.hibernate.TypeMismatchException: Provided id of the wrong type for class)
Question
How can I structure the entities (Person.java and Reference.java) in order to come up with the output below:
{
"id": 2001,
"name": "Moses",
"gender": "M",
"reference": {
"sep": 2001,
"guid": "EA48-...",
"tableName": "person"
}
}
Reference is the owner of the relationship and needs to be specified as such in either a unidirectional or bidirectional relationship
// Unidirection relationship
#Entity
public class Person implements Serializable {
#Id
private Long id;
private String name;
private String gender;
// Getters & Setters
}
#Entity
public class Reference implements Serializable {
#Id
private String guid;
private String tableName;
#OneToOne
#JoinColumn(name = "sep", insertable = false, updatable = false)
private Person person;
//Getters & Setters
}
// Bidirection relationship
#Entity
public class Person implements Serializable {
#Id
private Long id;
private String name;
private String gender;
#OneToOne(mappedBy = "person")
private Reference reference;
// Getters & Setters
}
#Entity
public class Reference implements Serializable {
#Id
private String guid;
private String tableName;
#OneToOne
#JoinColumn(name = "sep", insertable = false, updatable = false)
private Person person;
//Getters & Setters
}
Same example for read any kind records from table reference:
#Entity
#Table(name = "reference")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "table_name")
public abstract class AbstractReferenceEntity {
#Id
private UUID guid;
public UUID getGuid() {
return guid;
}
public void setGuid(UUID guid) {
this.guid = guid;
}
}
#Entity
#DiscriminatorValue("person")
public class PersonReferenceEntity extends AbstractReferenceEntity {
#OneToOne
#JoinColumn(name = "sep")
private Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
// Read all types of records.
AbstractReferenceEntity e = this.em.find(AbstractReferenceEntity.class, sameUuid));
// Read only person type of records.
AbstractReferenceEntity e = this.em.find(PersonReferenceEntity, sameUuid);
For the benefit of anyone looking to solve this kind of issue, I will be posting the solution that works for me following #XtremeBaumer suggestion in the comment.
Step 1: For the REFERENCE table, I made the JPA entity to have two ids (sep & table_name) by creating an extra composite Id class and using it in the Reference Entity.
public class RefId {
private Long sep;
private String tableName;
//All args constructor
//No args constructor
//Setters & Getters
//Override the equals() and hashCode() !very important
}
Step 2: Add the above class as a composite id to the Reference entity by using the #IdClass annotation. We must also declare and annotate the two fields with #Id in the Reference class.
#Entity
#Table(name="reference")
#IdClass(RefId.class) // Important if not using embeddable type
public class Reference implements Serializable {
#Id
private Long sep;
private String guid;
#Id
private String tableName;
//Getters & Setters
}
Step 3: In the Person entity, declare #OneToOne on the Reference entity and annotate it with #JoinColumnsOrFormulas as shown below:
#Entity
#Table(name="person")
public class Person implements Serializable {
#Id
private Long id;
private String name;
private String gender;
#OneToOne
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(column = #JoinColumn(name = "id", referencedColumnName = "sep", insertable = false, updatable = false)),
#JoinColumnOrFormula(formula = #JoinFormula(value = "'person'", referencedColumnName = "tableName"))
})
private Reference reference;
// Getters & Setters
}
This works fine in the scenario above. Note in the formula = #JoinFormula, it is like we are declaring the 'WHERE' clause i.e. WHERE table_name = 'person' (Don't miss the single quotes)
Lastly, by using the Jackson object mapper, I was able to get
{
"id": 2001,
"name": "Moses",
"gender": "M",
"reference": {
"sep": 2001,
"guid": "EA48-...",
"tableName": "person"
}
}
Thanks for your insight (#XtremeBaumer)

ManyToOne mapping not working when inside an Embeddable class

I am working on a project with Spring Boot (2.2.6.RELEASE) and JPA, and I am facing an issue that is bringing me nightmares...
I have a weak entity whose PK is a composite key of primary keys from parent tables.
+-----------------+
| BAR |
+-----------------+
| BAR_ID PK |
| NAME |
+-----------------+
|
| +-----------------+
| | FOO |
+------<+-----------------+
+------<| BAR_ID PK FK |
| | BAZ_ID PK FK |
| +-----------------+
|
+-----------------+
| BAZ |
+-----------------+
| BAZ_ID PK |
| NAME |
+-----------------+
So I have the following mapping...
#Entity
#Table(name = "FOO")
public class Foo implements Serializable {
#Data
#Embeddable
public static class ID implements Serializable {
#ManyToOne
#JoinColumn(name="BAR_ID")
private Bar bar;
#ManyToOne
#JoinColumn(name="BAZ_ID")
private Baz baz;
}
#EmbeddedId
private ID id;
}
#Entity
#Table(name = "BAR")
#SequenceGenerator(name = "SEQ_BAR", sequenceName = "SEQ_BAR")
public class Bar implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "SEQ_BAR")
#Column(name = "BAR_ID")
private Long id;
#Column(name = "NAME")
private String name;
}
#Entity
#Table(name = "BAZ")
#SequenceGenerator(name = "SEQ_BAZ", sequenceName = "SEQ_BAZ")
public class Baz implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "SEQ_BAZ")
#Column(name = "BAZ_ID")
private Long id;
#Column(name = "NAME")
private String name;
}
and the following Repository interface:
#Repository
public interface FooRepository extends JpaRepository<Foo, Foo.ID> {
}
then I try doing findById
fooRepository.findById(new Foo.ID(new Bar(1), new Baz(1));
and got the problem:
When I call fooRepository.findById(), although the row is fetched from database, ManyToOne mappings for Bar e Baz are not working properly. That means, whether I do foo.getId().getBar().getName() or foo.getId().getBaz().getName(), attribute name is null for both Bar and Baz objects (of course, the atrributes are not null in the database).
So my questions are:
This should work, right?
What am I doing wrong?
I have already tried changing fetch type to EAGER, but that didn't work at all.
I really appreciate any replies.
When I print database execution statement,the statement is Hibernate: select foo0_.bar_id as bar_id2_2_0_, foo0_.baz_id as baz_id1_2_0_ from foo foo0_ where foo0_.bar_id=? and foo0_.baz_id=?.Obviously the JPA does not query the Bar and Baz tables,so foo.getBar().getName() certainly will be null.
You can use follow way to achieve the same effect.
#Entity
#Table(name = "FOO")
#Data
public class Foo implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
#ManyToOne
#JoinColumn(name="BAR_ID")
private Bar bar;
#ManyToOne
#JoinColumn(name="BAZ_ID")
private Baz baz;
}
#Repository
public interface FooRepository extends JpaRepository<Foo, Long> {
Foo findByBarAndBaz(Bar bar, Baz baz);
}
use it like this:
Foo byId = fooRepository.findByBarAndBaz(new Bar(1L), new Baz(1L));
Bar bar = byId.getBar();
System.out.println(bar.toString());

How can I delete item which is on multiple side in spring-data-jpa?

There is a bidirectional one-to-many relationship between Department and Employee.
#Setter
#Getter
#Entity
#Table(name = "t_department")
public class Department {
#Id
private String id;
private String name;
#OneToMany(mappedBy = "department",fetch = FetchType.EAGER,cascade = CascadeType.ALL)
private List<Employee> employees;
}
#Setter
#Getter
#Entity
#Table(name = "t_employee")
public class Employee {
#Id
private String id;
private String name;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "dept_id")
private Department department;
}
#Repository
public interface EmployeeRepository extends JpaRepository<Employee, String> {
}
In the database, I have those records.
t_department:
+----+------------+
| id | name |
+----+------------+
| 2 | accounting |
| 3 | logistics |
+----+------------+
t_employee:
+----+------+---------+
| id | name | dept_id |
+----+------+---------+
| 3 | Tom | 2 |
| 4 | Tina | 3 |
+----+------+---------+
When I tried to delete an Employee(id="3"),
#Test
#Transactional
public void should_delete_employee_success_when_delete_employee_given_a_exist_employee_id_in_DB() {
employeeRepository.delete("3");
}
But in console, it only printed 2 select statements without deleting:
Hibernate: select employee0_.id as id1_2_0_, employee0_.dept_id as dept_id3_2_0_, employee0_.name as name2_2_0_, department1_.id as id1_1_1_, department1_.name as name2_1_1_ from t_employee employee0_ left outer join t_department department1_ on employee0_.dept_id=department1_.id where employee0_.id=?
Hibernate: select employees0_.dept_id as dept_id3_2_0_, employees0_.id as id1_2_0_, employees0_.id as id1_2_1_, employees0_.dept_id as dept_id3_2_1_, employees0_.name as name2_2_1_ from t_employee employees0_ where employees0_.dept_id=?
And I went to see the database, Nothing has been done.
How does spring-data-jpa works? I'm confused for several days.
Thank you for your answers in advance.
CrudRepository has methods delete(<entity>) and deleteById(). You should use deleteById and not the entity.
Because #Transactional Spring tests are by default marked for Rollback only and SQL changes are typically only flushed to the database on transaction commit.
You will then need to either (1) manually flush the changes to force a database write:
public class SomeTest{
#PersistenceContext
private EntityManager em;
#Test
#Transactional
public void should_delete_employee_success_when_delete_employee_given_a_exist_employee_id_in_DB() {
employeeRepository.delete("3");
em.flush(); //force db update however transaction will still be rolled back
}
or (2)
Set the transaction as not being for rollback only which you can do in various ways, including use of #Commit and #Rollback annotations:
https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#integration-testing-annotations-spring
I have found that once the department knows who is related to it, you can't delete employees who are related to the department. I use fetch=FetchType.EAGER on both sides, so when I delete the employee, it will load the department and the employees of the department. The right thing is that you need to remove the employee from the department's employees, then delete it. You can try this:
Optional<Employee> employeeOpt = employeeRepository.findById("3");
if (employeeOpt.isPresent()) {
Employee employee = employeeOpt.get();
employee.getDepartment().getEmployees().removeIf(emp -> emp.getId().equals("3"));
employeeRepository.deleteById("3");
}

create a reference table from two tables

I transform my site to use Java EE and hibernate!
I have 3 tables:
like
id | table | idElement | userId
movie
movie_id | title | duration
videoGame
game_id | title | nbPlayer
and currently 2 class:
#Entity
#Table(name="movie")
public class Movie {
#Id
#GeneratedValue
#Column(name="movie_id")
private Integer id;
private String title;
private int duration;
#Entity
#Table(name="videoGame")
public class Game {
#Id
#GeneratedValue
#Column(name="game_id")
private Integer id;
private String title;
private int nbPlayer;
I do not know how to make my table "like" to link it to one of the two table and the correct id?
example:
table like:
+---+-------+-----------+-------+
|id | table | idElement | userId|
|1 | movie | 1 | 1 |
|1 | game | 5 | 3 |
+---+-------+-----------+-------+
thanks a lot for your help!
Looks like there is no direct way to map your tables to hibernate (At least I am not aware of).
Using inheritance you can produce a similar effect. Movie and Game can be a subclass of another class (say Likable). So that you can use Likable class like below:
#Entity
#Table(name="like")
public class Like {
#Id
#GeneratedValue
#Column(name="like_id")
private Integer id;
#ManyToOne
#JoinColumn(name="likable_id", nullable=false)
private Likable likable;
#ManyToOne
#JoinColumn(name="user_id", nullable=false)
private User user;
}
Likable class will look like below:
#Entity
#Table(name = "likable")
#Inheritance(strategy=InheritanceType.JOINED)
public class Likable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "id")
private int id;
}
Movie class:
#Entity
#Table(name="movie")
#PrimaryKeyJoinColumn(name="id")
public class Movie extends Likable {
private String title;
private int duration;
}
And Game class:
#Entity
#Table(name="videoGame")
#PrimaryKeyJoinColumn(name="id")
public class Game extends Likable {
private String title;
private int nbPlayer;
}

JPA - Multiple columns discriminator

I have 3 tables like this in MySQL 5:
| PERSON |
| id | fullName | isEmp | isParent |
| EMPLOYEE |
| personId | code |
| PARENT |
| personId | job |
in which, Employee.personId and Parent.personId are foreign keys pointing to Person.id. An employee can also be a parent and vice versa. So how can I config using Annotation of JPA 2.0/Hibernate 3? Thanks!
If a Person can be both, you can't solve this through inheritance, because Java doesn't allow multiple inheritance. So you'll have to go with Aggregation, which is confusing on a semantic level, because it's has-a-parent instead of is-a-parent. But I'm afraid it's the way you'll have to go:
#Entity
public class Person{
#Id
private Long id;
#OneToOne(optional=true)
private Employee employee;
#OneToOne(optional=true)
private Parent parent;
public boolean isParent(){return parent!=null;}
public boolean isEmployee(){return employee!=null;}
}
#Entity
public class Employee{
#Id
private Long id;
#OneToOne(mappedBy="employee",optional=false)
private Person person;
}
#Entity
public class Parent{
#Id
private Long id;
#OneToOne(mappedBy="parent",optional=false)
private Person person;
}
(getters / setters etc. omitted)

Categories

Resources