Get Hibernate single iteration values only in an entity - spring

I need to get values from user table, in that user have an manager id,
Manager is an user so again manager id is mapped with user entity.
This will cause continuous iteration until the manager ID get null.
\
The entity get more on inside on it > User entity > manager id> --> user entity ....
Is there any possible to get single entity with single manager using hibernate query?
USER_NAME (PK)
CREATE_DATE
UPDATED_DATE
ROLE_ID
USER_ID
MANAGER_ID
REGION_ID
USER_GROUP
Created_By
User class
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "USER_ID", unique = true, nullable = false)
private Long userId;
#ManyToOne( targetEntity = User.class, cascade = CascadeType.ALL)
#JoinColumn(name = "MANAGER_ID", referencedColumnName = "USER_ID")
private User managerId;

You can relate to entity only managerId(I mean Long type), or you can mark Many to one relation with fetch = FetchType.LAZY and call this field only if needed.

Related

Spring Boot JPA - find by 2 columns

I don't know how to create a JPA query to get all records from my table:
#Entity
#Table(name = "A")
public class A{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, updatable = false)
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "message_id")
private Message;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
}
#Entity
#Table(name="message")
#Getter
#Setter
public class Message{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, updatable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "account_id", foreignKey = #ForeignKey(name = "FK_account_to_message"))
private Account account; //and then search by Account.id
}
SO I have 2 types of objects in A table (sometimes object is created from email, sometimes from a file):
one type without user_id (null) -> then I have to find all A object by searchning by Message -> Account -> Id
second type with user_id -> we can directly get A objects by values in user_id column
I want to get all records for specific user_id -> how to do that in most efficient way? I don't want to invoke 2 methods in repository:
User user = userService.getEmail();
List<A> aObjects= Stream.concat(ARepository.findByMessage_Account_Id(user.getId()).orElse(new ArrayList<>()).stream(),
aRepository.findByUser_Id(user.getId()).orElse(new ArrayList<>()).stream()).collect(Collectors.toList());
Is it possible to create ONE repository method that finds all records for 2 different objects (one with user_id and second without user_id)?
I guess that this query is to complex for using derived from the method name query. As it stated in the documentation:
Although getting a query derived from the method name is quite convenient, one might face the situation in which either the method name parser does not support the keyword one wants to use or the method name would get unnecessarily ugly. So you can either use JPA named queries through a naming convention or rather annotate your query method with #Query.
So, I would suggest just write the following query:
#Query("select aa from A aa left join aa.user u left join aa.message msg left join msg.account acc where (u is null and acc is not null and acc.id = :userId) or (u is not null and u.id = :userId)")
List<A> findByUserOrAccountId(#Param("userId") Long userId);

ManyToMany relationship removes all entities

I have two entities, user and movies. They're manytomany bidirectional relationship. My problem is that when I delete a movie via my controller it also removes all related with that movie entities. My user and this user roles and the movie entity. What have I to do, to get rid just off movie entity from the table when delete it and keep the user with his roles instead of removing them all.
#Data
#ToString
#EqualsAndHashCode
#Entity
#Table(name = "movies")
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "title")
private String title;
#Column(name = "description")
private String description;
#Column(name = "release_date")
#Temporal(TemporalType.DATE)
private Date release_date;
#Column(name = "country")
private String country;
#Column(name = "category")
private String category;
#ManyToMany(mappedBy = "movies")
#ToString.Exclude
#EqualsAndHashCode.Exclude
private Set<User> users = new HashSet<>();
#Data
#ToString
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Transient
private String confirmPassword;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "users_roles", joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "role_id", referencedColumnName = "id")})
#ToString.Exclude
#EqualsAndHashCode.Exclude
private Set<Role> roles = new HashSet<>();
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "users_movies", joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "movie_id", referencedColumnName = "id")})
#ToString.Exclude
#EqualsAndHashCode.Exclude
private Set<Movie> movies = new HashSet<>();
this is the expected behavior when you use CascadeType.ALL, which implicitly includes CascadeType.REMOVE - that removes all the related entities on removing the owning entity.
Since you are using CascadeType.ALL on both sides of the association, as a result you end up deleting records more than what you actually intended.
To avoid this:
You need to remove the CascadeType.ALL or change it to something appropriate like MERGE / PERSIST
Handle the deletion of entities on your own.
Consider using batch delete option specifying the associated entity id.
Problem solved, instead of using mappedBy I added a JoinTable annotation also to Movie entity and now it works as it should.
Well, I had to guess what your Role entity looks like so include complete examples. You didn't specify whether you were using Spring-Data-Jpa or just Jpa so who knows. Edit: in your examples you have finally clarified that you are using spring-data-jpa, but the concepts are the same as here anyway.
#Data
#ToString
#Entity
#Table(name = "roles")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(mappedBy="roles")
#ToString.Exclude
#EqualsAndHashCode.Exclude
private Set<User> users = new HashSet<>();
}
and to use
tx.begin();
User u1 = new User();
Role r1 = new Role();
u1.setRoles(Collections.singleton(r1));
Movie m1 = new Movie();
m1.setDescription("movie 1");
Movie m2 = new Movie();
m2.setDescription("movie 2");
u1.setMovies(Stream.of(m1, m2).collect(Collectors.toSet()));
em.persist(u1);
tx.commit();
// now to query
em.clear();
tx.begin();
User u = em.createQuery("from User u left outer join fetch u.movies where u.id = 1", User.class).getSingleResult();
Movie m = u.getMovies().stream().filter(mv->mv.getDescription().equals("movie 1")).findFirst().get();
u.getMovies().remove(m);
em.remove(m);
tx.commit();
and this creates the log
create table users (id bigint generated by default as identity (start with 1), password varchar(255), username varchar(255), primary key (id))
create table users_movies (user_id bigint not null, movie_id bigint not null, primary key (user_id, movie_id))
create table users_roles (user_id bigint not null, role_id bigint not null, primary key (user_id, role_id))
alter table users_movies add constraint FKt4hasm7tvj0vor58ql33xptjy foreign key (movie_id) references movies
alter table users_movies add constraint FKhhj9vi206o88q0typfntk3fek foreign key (user_id) references users
alter table users_roles add constraint FKj6m8fwv7oqv74fcehir1a9ffy foreign key (role_id) references roles
alter table users_roles add constraint FK2o0jvgh89lemvvo17cbqvdxaa foreign key (user_id) references users
insert into users (id, password, username) values (default, ?, ?)
insert into movies (id, category, country, description, release_date, title) values (default, ?, ?, ?, ?, ?)
insert into movies (id, category, country, description, release_date, title) values (default, ?, ?, ?, ?, ?)
insert into roles (id) values (default)
insert into users_movies (user_id, movie_id) values (?, ?)
insert into users_movies (user_id, movie_id) values (?, ?)
insert into users_roles (user_id, role_id) values (?, ?)
select user0_.id as id1_2_0_, movie2_.id as id1_0_1_, user0_.password as password2_2_0_, user0_.username as username3_2_0_, movie2_.category as category2_0_1_, movie2_.country as country3_0_1_, movie2_.description as descript4_0_1_, movie2_.release_date as release_5_0_1_, movie2_.title as title6_0_1_, movies1_.user_id as user_id1_3_0__, movies1_.movie_id as movie_id2_3_0__ from users user0_ left outer join users_movies movies1_ on user0_.id=movies1_.user_id left outer join movies movie2_ on movies1_.movie_id=movie2_.id where user0_.id=1
delete from users_movies where user_id=? and movie_id=?
delete from movies where id=?

Jpa ManytoMany issue with Spring Boot

I have a postgres database and I am trying to make a simple REST service with Spring Boot. I have a problem with jpa ManytoMany relationship.
Person Entity:
#Entity
#Table(name = "person", schema = "persons")
public class Person implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String email;
#Column
private Integer age;
#ManyToOne
#JoinColumn(name = "country_id", referencedColumnName = "id")
private Country countryOfBirth;
#ManyToMany
#JoinTable(
name="persons_countries_residence",
joinColumns=#JoinColumn(name="person_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="country_id", referencedColumnName="id"))
private List<Country> countriesOfResidence;
// getters and setters and to String method overriden
}
Country Entity:
#Entity
#Table(name = "country", schema = "persons")
public class Country implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#Column(name = "country_name")
private String name;
#Column(name = "country_code")
private String code;
// getters and setters and to String method overriden
}
The postgres schema is the following:
Person Table:
CREATE TABLE persons.person
(
id serial NOT NULL,
name character varying(50) NOT NULL,
email character varying(40) NOT NULL,
age integer,
country_id serial NOT NULL,
CONSTRAINT id PRIMARY KEY (id),
CONSTRAINT country_id FOREIGN KEY (id)
REFERENCES persons.country (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Country table:
CREATE TABLE persons.country
(
id serial NOT NULL,
country_name character varying(45) NOT NULL,
country_code character varying(10) NOT NULL,
CONSTRAINT country_id PRIMARY KEY (id)
)
Join table:
CREATE TABLE persons.persons_countries_residence
(
person_id integer NOT NULL,
country_id integer NOT NULL,
CONSTRAINT person_country_id PRIMARY KEY (person_id, country_id),
CONSTRAINT persons_countries_residence_country_id_fkey FOREIGN KEY (country_id)
REFERENCES persons.country (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE NO ACTION,
CONSTRAINT persons_countries_residence_person_id_fkey FOREIGN KEY (person_id)
REFERENCES persons.person (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE
)
When i make an HTTP method call, I don't get the Countries of residence.
Service code:
#RequestMapping(method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public List<Person> getAllPersons() {
retutn jpaPersonRepository.findAll();
}
Any help appreciated.
Maybe, you need to specify a schema name in the join table name:
#JoinTable(
name="persons_countries_residence", schema="persons",
joinColumns=#JoinColumn(name="person_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="country_id", referencedColumnName="id"))
Update your Country class code like :
#Entity
#Table(name = "country", schema = "persons")
public class Country implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#Column(name = "country_name")
private String name;
#Column(name = "country_code")
private String code;
#ManyToMany(mappedBy = "countriesOfResidence")
private List<Person> persons;
// getters and setters and to String method overriden
}
Although a ManyToMany relationship is always bi-directional on the
database, the object model can choose if it will be mapped in both
directions, and in which direction it will be mapped in. If you choose
to map the relationship in both directions, then one direction must be
defined as the owner and the other must use the mappedBy attribute to
define its mapping. This also avoids having to duplicate the JoinTable
information in both places.
Do you mean that the country list is null? #ManyToMany associations are lazily loaded by default, you need to enable eager-fetching for it to work straight away.
#ManyToMany(fetch = FetchType.EAGER)
The solution is this:
#ManyToMany
#JoinTable(
name="persons_countries_residence", schema = "persons",
joinColumns=#JoinColumn(name="person_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="country_id", referencedColumnName="id"))
private List<Country> countriesOfResidence;
The schema had to be specified at the #JoinTable annotation as well.

Cascade remove with Spring Data JPA (bi-directional)

I've got the following bi-directional relationship between two entity classes:
User
#Entity
#Table(name = "USER")
public class User {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
#Cascade({CascadeType.REMOVE, CascadeType.SAVE_UPDATE})
private Set<Book> books;
}
Book
#Entity
#Table(name = "BOOKS")
public class Book {
#ManyToOne(optional = false)
#JoinColumns({
#JoinColumn(name = "ID", referencedColumnName = "ID"),
#JoinColumn(name = "NAME", referencedColumnName = "NAME"),
})
private User user;
}
I want to remove a User with the delete cascading down to all the books associated with the user. However, when I used a Spring Data CrudRepository:
myDAO.delete(String userId) // interface extending CrudRepository<User, UserPK>
I'm getting:
Caused by: org.hsqldb.HsqlException: integrity constraint violation: foreign key no action; FK_IN88FEHUXOOYQK0YE71USPIEP table: USER
I was trying to use orhpanRemoval, all kind of CascadeTypes both org.hibernate and javax.persistence. I don't want to implement native queries like #Query(Delete from User....) I'm wondering about exception statement foreign key no action whether I directly specified cascade option.
There is a many-to-one relationship from Book to User. Therefore, the Book table requires only a single column to capture the foreign key to the User table.
The code:
#ManyToOne(optional = false)
#JoinColumns({
#JoinColumn(name = "ID", referencedColumnName = "ID"),
#JoinColumn(name = "NAME", referencedColumnName = "NAME"),
})
private User user;
should simply be:
#ManyToOne(optional = false)
#JoinColumn(name = "user_id")
private User user;
When you use the Hibernate Schema Generation Tool (HBM2DDL), foreign key constraints are generated as follows:
ALTER TABLE
BOOK
ADD CONSTRAINT
FK_IN88FEHUXOOYQK0YE71USPIEP
FOREIGN KEY
(USER_ID) REFERENCES USER (ID)
ON UPDATE NO ACTION
ON DELETE NO ACTION;
Notice ON DELETE NO ACTION. This means, when a User instance is deleted, nothing should happen to Book instances associated with it (NO ACTION). This practically means that the User should not be allowed to be deleted.
If you want Book instances associated with a User to be deleted when the User is deleted, the DDL should be:
ALTER TABLE
BOOK
ADD CONSTRAINT
FK_IN88FEHUXOOYQK0YE71USPIEP
FOREIGN KEY
(USER_ID) REFERENCES USER (ID)
ON UPDATE NO ACTION
ON DELETE CASCADE;
This can be generated by adding a Hibernate-specific annotation to the domain model:
class User {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
#Cascade({CascadeType.REMOVE, CascadeType.SAVE_UPDATE})
#OnDelete(OnDeleteAction.CASCADE)
private Set<Book> books;
}

Exception JPA/Hibernate: detached entity passed to persist while saving child #ManyToOne relation

Unable to save child object reference.
Employee parent object contains the child employee_detail which also has a #ManyToOne defined to save Address object.
Table structure
EMPLOYEE
ID BIGINT(20) NOT NULL AUTO_INCREMENT
NAME VARCHAR(100) NOT NULL
EMPLOYEE_DETAIL
ID BIGINT(20) NOT NULL AUTO_INCREMENT
EMPLOYEE_ID BIGINT(20) NOT NULL
ADDRESS_ID BIGINT(20) NOT NULL
Entities
#Entity
#Table(name = "employee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List < EmployeeDetail > employeeDetails = new ArrayList < > ();
public void addEmployeeDetail(EmployeeDetail ed) {
employeeDetails.add(ed);
ed.setEmployee(this);
}
}
#Entity
#Table(name = "employee_detail")
public class EmployeeDetail {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#ManyToOne(optional = false, cascade = CascadeType.PERSIST)
#JoinColumn(name = "employee_id")
private Employee employee;
#ManyToOne(optional = false, cascade = CascadeType.PERSIST)
#JoinColumn(name = "address_id")
private Address address;
}
In the REST Controller method:
public void saveEmployee(#RequestBody Employee employee)
{
EmployeeDetail employeeDetail = new EmployeeDetail();
employeeDetail.setEmployee(employee);
for(EmployeeDetail ed : employee.getEmployeeDetails())
{
Address address = addressService.findOne(ed.getAddress().getId());
employeeDetail.setAddress(address);
}
employee.addEmployeeDetail(employeeDetail);
//SAVE
employeeService.create(employee);
}
Exception:
nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.abc.Address
I am unable to save the Address details with the child table EmployeeDetail. What is wrong here?
Apply CascadeType.MERGE too for Address entity as below
#ManyToOne(optional = false, cascade = {CascadeType.PERSIST,CascadeType.MERGE})
#JoinColumn(name = "address_id")
private Address address;
and use merge() instead of persist to save your changes
EDIT
Yes you will have to apply the CascadeType.MERGE for EmployeeDetail too .. otherwise you will get the same exception if you have a detached EmployeeDetail instance in you network of objects starting from Employee.
There are couple of scenarios which you will need to consider.As explained below.
When you call persist() on your root entity all the entity association mapped with CascadeType.PERSIST are also passed to the persist() (transitive peristence) . Now if any of your entity in the object graph is detached persist() will be called for that entity too and you will get an exception for that entity.
In order to save your complete graph of objects which includes both detached and transient instances , mark your association with CascadeType.MERGE and call merge() on your root entity .. Now merge() operation will be cascaded to all the entity association mapped with CascadeType.MERGE. The behaviour of merge is that it will merge any detached entity being passed to it to the existing entity in the persistence context or if the entity is transient it will make it persistent.
So while trying to save you entity graph you need to choose whether to use persist() or merge() based on the info whether the entity graph you are going to save has only transient instances or it has both transient and detached instances.You will also have to consider the cascade type set on each association.
For your code .. i see the Address instance you are setting on the EmployeeDetail instance is detached as you are getting it from another already fetched EmplyeeDetail instance.
You can get more detail on below link
JPA EntityManager: Why use persist() over merge()?
You can also extend your persistence context by creating conversations if you dont want to work with detached instances.More on this below
http://www.thoughts-on-java.org/unsychronized-persistencecontext-implement-conversations-jpa/

Resources