How to implements entity with 2 entity as primary key with jpa annotation and repository - spring

i want to implement a many to many association with quantity information in it . like this :
#Entity
#Table(name = "reserves")
#Getter #Setter #NoArgsConstructor
public class Reserve {
#Id
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "groupe_id")
private GroupeSanguin bloodGroup;
#Id
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Banque banque;
private int quantity;
}
the GroupSanguin and the Banque are two class stored in the database two . here is the code for the two if you need :
#Entity
#Table(name = "groupe_sanguins")
public class GroupeSanguin {
#Id
private String groupe;
#OneToMany(mappedBy = "groupeSanguin")
private List<Donneur> donneurs;
}
#Entity #Getter #Setter #NoArgsConstructor
public class Banque {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true,nullable = false)
private String nom;
private String adresse;
#Column(unique = true)
private String telephone;
private String localisation;
}
so my i want to know how to annotate the JpaRepository to take the two as primary key like this and is my annotation good for it to work ?
public interface ReserveRepository extends JpaRepository<
Reserve,
//what to put here ?
>

This isn't a JPA question in fact, it's a relationnal database conception.
If Reserve has is own data and links with other entity it has it own Id
You can add unicity constraint
#Entity
#Table(name = "reserves", uniqueConstraints={
#UniqueConstraint(columnNames = {"banque_id", "groupe_id"})
#Getter #Setter #NoArgsConstructor
public class Reserve {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "groupe_id")
private GroupeSanguin bloodGroup;
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "banque_id")
private Banque banque;
private int quantity;
}

i've found this solutions too.
#Entity
#Table(name = "reserves")
#Getter #Setter #NoArgsConstructor
#IdClass(ReserveId.class) //this annotation will tell that id that the
// the id will be represented by a class
public class Reserve {
#Id
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "groupe_id")
private GroupeSanguin groupeSanguin;
#Id
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "banque_id")
private Banque banque;
private int quantity;
}
and the id class should implements Serializable like this :
#Getter #Setter
public class ReserveId implements Serializable {
private Banque banque;
private GroupeSanguin groupeSanguin;
}
and finally the repository will be like that :
#Repository
public interface ReserveRepo extends JpaRepository<Reserve, ReserveId>{}

See your Reserve class has nowhere mentioned composite primary key. First you need to fix the model, You can refer to the solution here How to create and handle composite primary key in JPA

Related

Add extra custom column to auto mapped Table in Spring JPA ManyToMany

#Getter #Setter #NoArgsConstructor #AllArgsConstructor
#Table(name = "my_users")
public class MyUsers {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
private Long id;
#Column(nullable = false, unique = true)
private String userName;
private String password;
#ManyToMany
private List<MyUsers> connections;
}
This is my MyUsers Model Class. I am using Hibernate and MySQL.
#ManyToMany
private List<MyUsers> connections;
This ManyToMany relationship is automatically creating the table 'my_users_connections' with 'my_users_id' and 'connections_id' colums. How can I add extra columns to this auto mapped table?
It's not ideal solution...
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "my_users")
public class MyUsers implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
private Long myUsersId;
#Column(nullable = false, unique = false)
private String userName;
private String password;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "my_users_connections",
joinColumns = { #JoinColumn(name = "my_users_id") },
inverseJoinColumns = { #JoinColumn(name = "connections_id") })
private List<MyUsers> connections;
}
Create embedded id MyUsersConnectionsPK:
#Data
#Embeddable
public class MyUsersConnectionsPK implements Serializable {
#Column(name = "my_users_id")
private Long myUsersId;
#Column(name = "connections_id")
private Long connectionsId;
}
Create MyUsersConnections, which represent ManyToMany
#Data
#Entity
#Table(name = "my_users_connections")
public class MyUsersConnections implements Serializable {
#EmbeddedId
private MyUsersConnectionsPK id;
#ManyToOne
#MapsId("my_users_id")
#JoinColumn(name = "my_users_id")
private MyUsers myUsersId;
#ManyToOne
#MapsId("connections_id")
#JoinColumn(name = "connections_id")
private MyUsers connectionsId;
#Column(name = "extra_column")
private String extraColumn;
}
Create JPA repository
#Repository
public interface MyUsersConnectionsRepository extends JpaRepository<MyUsersConnections, MyUsersConnectionsPK> {
List<MyUsersConnections> findMyUsersConnectionsByMyUsersIdMyUsersId(Long id);
}
And simple sample for using:
#Service
public class Test {
#Autowired
private MyUsersConnectionsRepository myUsersConnectionsRepository;
#Autowired
private MyUsersRepository myUsersRepository;
public void test() {
MyUsers myUsers = new MyUsers();
myUsers.setUserName("user name");
myUsers.setPassword("password");
MyUsers myUsers2 = new MyUsers();
myUsers2.setUserName("user name 2");
myUsers2.setPassword("password 2");
myUsers.setConnections(Collections.singletonList(myUsers2));
myUsers = myUsersRepository.saveAndFlush(myUsers);
List<MyUsersConnections> myUsersConnections = myUsersConnectionsRepository.findMyUsersConnectionsByMyUsersIdMyUsersId(myUsers.getMyUsersId());
MyUsersConnections item = myUsersConnections.get(0);
item.setExtraColumn("Extra column");
myUsersConnectionsRepository.saveAndFlush(item);
}
}

(Do not display relationship values)

I have two entity with name of the article and article Category.
and they have one-to-many relationships.
I use #JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class,property = "id")
but I cant see data of article category(category_id) in spring data rest.
ArticleCategory.class
#Entity
#Table(name = "article_category")
#Getter
#Setter
public class ArticleCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "articleCategory", fetch = FetchType.LAZY)
private Set<Article> articles = new HashSet<>();
}
Article.class
#Entity
#Table(name = "article")
#Getter
#Setter
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Article implements Serializable {
public Article() {
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id", nullable = false)
private ArticleCategory articleCategory;
#Column(name = "title")
private String title;
#Column(name = "image_url")
private String image_url;
#Column(name = "short_description")
private String short_description;
#Column(name = "text")
private String text;
#Column(name = "keywords", nullable = true)
private String keywords;
#Column(name = "visit", nullable = false)
private int visit;
#Column(name = "code", nullable = false)
private UUID code;
#Column(name = "date_created")
#CreationTimestamp
private Date dateCreated;
#Column(name = "date_updated", nullable = false)
#UpdateTimestamp
private Date dateUpdated;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
public Article(String title, String image_url, String short_description, String text, String keywords, int visit, UUID code) {
this.title = title;
this.image_url = image_url;
this.short_description = short_description;
this.text = text;
this.keywords = keywords;
this.visit = visit;
this.code = code;
}
}
Article Repository
#CrossOrigin("http://localhost:4200")
#RepositoryRestResource(collectionResourceRel = "article", path = "article")
public interface ArticleRepository extends JpaRepository<Article,Long> {
Article findByCode(UUID uuid);
}
And this is output of spring data rest
enter image description here
That is exactly because you used #JsonManagedReference and #JsonBackReference. Keep in mind the following when using them:
#JsonManagedReference is the forward part of the relationship and is the one that gets serialized normally.
#JsonBackReference is the back part of the relationship and it will be omitted from serialization.
The serialized Article object does not contain a reference to the ArticleCategory object.
If you want to have any ArticleCategory data when serializing Article you can either use #JsonIdentityInfo so that one of the properties is serialized (in this case I've chosen id for both):
#Entity
#Table(name = "article")
#Getter
#Setter
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Article implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id", nullable = false)
private ArticleCategory articleCategory;
}
#Entity
#Table(name = "article_category")
#Getter
#Setter
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class ArticleCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#OneToMany(cascade = CascadeType.ALL,mappedBy = "articleCategory" ,fetch = FetchType.LAZY)
private Set<Article> articles=new HashSet<>();
}
If you are only interested in categoryId another possibility would be to use #JsonIgnore on private Set<Article> articles property so that it is not serialized:
#Entity
#Table(name = "article_category")
#Getter
#Setter
public class ArticleCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#JsonIgnore
#OneToMany(cascade = CascadeType.ALL,mappedBy = "articleCategory" ,fetch = FetchType.LAZY)
private Set<Article> articles=new HashSet<>();
}
If none of those suits your needs you might need to implement your own custom serializer. You can read more about all those options at https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion.
I solved the problem using the controller
And that's why #JsonManageRefrence and #JsonBackRefrence do not work
I replaced the lazy load with the eager load in both entity
#ManyToOne(fetch = FetchType.Eager)
#JoinColumn(name = "user_id")
#JsonManageRefrence
private User user;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "articleCategory",
fetch = FetchType.Eager)
#JsonBackRefrence
private Set<Article> articles = new HashSet<>();
and then add a controller
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#RestController
#RequestMapping("/getAllArticle")
public class MyController {
private ArticleRepository articleRepository;
// you must do constructor injection
#GetMapping("/getAllArticle")
public List<Article> allArticle()
{
return articleRepository.findAll();
}
}

FetchType.LAZY in ManyToMany doesnot work

I'm having a problem with, fetch = FetchType.LAZY, it just doesn't work. I've already spent a lot of time solving this problem, can anyone help with this? I'll be very thankful. I have a genre and a country that are associated with movie manyTomany. No matter how hard I try to initialize the LAZY download, it doesn't work.I need the movie to have EAGER, and the genre and country to have LAZY.
I expect to get movie with its genre and country, But with SELECT * FROM movie WHERE id = 1 - I get an endless loop, although genre and country has LAZY download.
Sample code - below
Entities:
Movie
#Entity
#Getter
#Setter
#ToString(of = {"id", "year", "name"})
#EqualsAndHashCode(of = {"id", "year"})
#NoArgsConstructor
#AllArgsConstructor
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
**********
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "movie_genre",
joinColumns = {
#JoinColumn(name = "movie_id")},
inverseJoinColumns = {
#JoinColumn(name = "genre_id")})
private Set<Genre> genres = new HashSet<>();
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "movie_country",
joinColumns = {
#JoinColumn(name = "movie_id")},
inverseJoinColumns = {
#JoinColumn(name = "country_id")})
private Set<Country> countries = new HashSet<>();
}
Genre
#Entity
#Getter
#Setter
#ToString(exclude = "movies")
#EqualsAndHashCode(exclude = "movies")
#NoArgsConstructor
#AllArgsConstructor
public class Genre {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Size(max = 20)
private String name;
#ManyToMany(mappedBy = "genres")
private Set<Movie> movies = new HashSet<>();
}
Country
#Entity
#Getter
#Setter
#ToString(exclude = "movies")
#EqualsAndHashCode(exclude = "movies")
#NoArgsConstructor
#AllArgsConstructor
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Size(max = 20)
private String name;
#ManyToMany(mappedBy = "countries")
private Set<Movie> movies = new HashSet<>();
}
Controller
#RestController
public class TestController {
#Autowired
private MovieService movieService;
#Autowired
private CountryService countryService;
#Autowired
private GenreService genreService;
#GetMapping("movie")
public List<Movie> getMovieMovie(){
return movieService.getAll();
}
#GetMapping("movie/add")
public Movie create(){
Movie movie = new Movie();
movie.setName("test");
movie.setImg("test");
movie.setTime("test");
movie.setYear((short) 2332);
movie.setMovieLink("test");
movie.getCountries().add(countryService.getCountry(1));
movie.getGenres().add(genreService.getGenre(1));
return movieService.create(movie);
}
}
Service
#Service
public class MovieService {
#Autowired
private MovieRepository movieRepository;
public List<Movie> getAll(){
return movieRepository.findAll();
}
#Transactional
public Movie create(Movie mocie){
return movieRepository.save(mocie);
}
}
Lazy loading works as expected, as it loads all data lazy.
What you are looking for is a way to break loop in the bi-directional mapping.
There you can use #JsonManagedReference and #JsonBackReference that you have to set on the relationships.
Please also read this: https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion

JPA Composite PK referenced by FKs in One to Many Relationship

I am trying to map the tables below into JPA. The relationships are one-to-many between user_tax and tax and user_tax and user. It has confused me the fact that i have a composite primary key, and i need to map the foreign keys to these 2 keys.
the error message: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: entity.Tax.user_tax in entity.UserTax.taxs
tax user_tax user
-------- -------- ------
PK|t_id |--------| t_id |PK-FK |u_name|
|t_name| PK-FK| u_id |-------|u_id | PK
| | | name | | |
Here is my Entities:
#Entity
#Table(name = "user")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="u_name")
private String uname;
getters + setters
}
#Entity
#Table(name = "tax")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Tax implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "t_name")
private String tname;
#Embeddable
public class UserTaxId implements Serializable {
#Column(name="u_id")
private Long uId;
#Column(name="t_id")
private Long tId;
#Entity
#Table(name = "user_tax")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class UserTax implements Serializable {
#EmbeddedId
private UserTaxId userTaxId;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user_tax")
private List<User> users;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user_tax")
private List<Tax> taxs;
Your 1:n mappings are backwards (i.e. a UserTax can have only a single User and a single Tax) and you are using a derived identity. Try mapping UserTax like this:
#Entity
#Table(name = "user_tax")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class UserTax implements Serializable {
#EmbeddedId
private UserTaxId userTaxId;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("uId") // maps uId attribute of embedded id
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("tId") // maps tId attribute of embedded id
private Tax tax;
...
}
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.
I will post here what worked for me after 3 days of research.
Brian Vosburgh correctly posted the UserTax class:
#Entity
#Table(name = "user_tax")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class UserTax implements Serializable {
#EmbeddedId
private UserTaxId userTaxId;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("uId") // maps uId attribute of embedded id
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("tId") // maps tId attribute of embedded id
private Tax tax;
...
}
However, i was getting error meassages and my code wasn't compiling. Then i also had to edit the User and Tax classes:
#Entity
#Table(name = "user")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(
mappedBy = "tid",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<UserTax> tax = new ArrayList<>();
#Column(name="u_name")
private String uname;
getters + setters
}
#Entity
#Table(name = "tax")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Tax implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(
mappedBy = "uid",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<UserTax> taxs = new ArrayList<>();
#Column(name = "t_name")
private String tname;
setters+getters
}
Here is the link where i found the solution to my problem: https://vladmihalcea.com/the-best-way-to-map-a-many-to-many-association-with-extra-columns-when-using-jpa-and-hibernate/

Jpa findAllBy* using Long id instead of an entity

I just do not want to do:
myEntity = findById(Long id)
findAllByEntityAnd*(MyEntity myEntity, *)
instead I want do:
findAllByEntityAnd*(Long entityId, *)
Is there something I missed to achieve what I wanted or I just cannot achieve it directly?
Thanks for the help ~
When I tried it, the Spring prompted me:
java.lang.IllegalArgumentException: Could not create query metamodel for method public abstract java.util.List com.worksap.morphling.raptor.dump.thread.dao.ThreadDoRepository.findAllByDumpDoAndLocksWaitingContains(java.lang.Long,java.lang.String)!
Here is my table for your reference:
#Data
#Builder
#Entity
#Table(name = "thread_info")
#AllArgsConstructor
#NoArgsConstructor
public class ThreadDo implements Serializable {
private static final long serialVersionUID = -1L;
String name;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "thread_dump_id")
#JsonIgnore
private ThreadDumpDo dumpDo;
...
}
#Data
#Builder
#Entity
#Table(name = "thread_dump")
#AllArgsConstructor
#NoArgsConstructor
public class ThreadDumpDo implements Serializable {
private static final long serialVersionUID = -1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(
mappedBy = "dumpDo",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<ThreadDo> threadDoList;
}
And then I have a ThreadDoRepository and want to locate the threadDos directly like this:
List<ThreadDo> findAllByDumpDoAndLocksWaitingContains(Long dumpId, String lockWaiting);
I can achieve it via #Query as follows, but I really think it's pretty ugly since it's easy to interpret (I think).
#Query(value = "select * from thread_info t where t.thread_dump_id = ?1 and t.locks_waiting like ?2",
nativeQuery = true)
List<ThreadDo> findAllByDumpDoAndLocksWaitingContains(Long dumpId, String lockWaiting);
Try this
List<ThreadDo> findAllByDumpDo_IdAndLocksWaitingContains(Long dumpId, String lockWaiting);

Resources