#Entity
#Table(name = "clusters", uniqueConstraints = {#UniqueConstraint(name = "unique_cluster_name",
columnNames = {"clusterName"})})
#Builder
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class ClusterEntity extends BaseEntity {
#OneToMany(mappedBy = "cluster",
cascade = CascadeType.REMOVE)
private List<CorePhraseEntity> phrases = new ArrayList<>();
}
#Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Table(name = "phrases", uniqueConstraints = {#UniqueConstraint(name = "unique_phrase",
columnNames = {"phrase"})})
public class CorePhraseEntity extends BaseEntity implements Phraseable {
private static final String URL_PREFIX = "/semantics/phrases/core/";
#ManyToOne
#JoinColumn(name = "cluster_id",
nullable = false,
foreignKey = #ForeignKey(name = "cluster_fk"))
private ClusterEntity cluster;
#NotNull
#NotEmpty
#Column(nullable = false,
columnDefinition = "varchar(1000) default ''")
private String phrase;
#NotNull
#Column(nullable = false,
columnDefinition = "INTEGER DEFAULT -1")
Integer frequency;
}
Could you tell me what should I do for cluster.getPhrases() to select phrases ordered by frequency?
try
#OneToMany(mappedBy = "cluster", cascade = CascadeType.REMOVE)
#OrderBy("frequency")
private List<CorePhraseEntity> phrases = new ArrayList<>();
see here
How do you order a oneToMany join table in hibernate criteria
Related
I'm developing an application that queries a database.
There are a few issues right now.
history.isPresent() == false when calling Optional<History> findByKeyEquals() intermittently. but value is exist on database
This is the information I got while tracking the issue.
All child entities are non-null.
In most cases, if the same function is re-executed, it is searched.
But sometimes it doesn't happen intermittently.
i think that i use incorrectly table relationship annotation (#OneToMany,#ManyToOne options..)
I want to solve this issue.
this is my code
History (Parent)
#Table(
indexes = {
#Index(columnList = "key", unique = true),
})
#Entity
#Getter
#ToString
#Audited
public class History implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false, columnDefinition = "BIGINT UNSIGNED")
private Long id;
#Setter
#Column(nullable = false, columnDefinition = "CHAR(36)")
private String key = UUID.randomUUID().toString();
#Setter
#Temporal(TemporalType.TIMESTAMP)
#NotAudited
private Date started = new Date();
#Setter
#Temporal(TemporalType.TIMESTAMP)
#NotAudited
private Date finished;
#Setter
#OneToMany(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "history",
orphanRemoval = true)
#NotAudited
private List<Content> contents = new ArrayList<>();
...
}
Content (Child)
#Table
#Entity
#Getter
#Audited
public class Content implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false, columnDefinition = "BIGINT UNSIGNED")
private Long id;
#Setter
#Column(columnDefinition = "LONGTEXT")
#NotAudited
private String content;
#Setter
#ManyToOne(targetEntity = History.class, fetch = FetchType.Lazy, optional = false)
#Audited
private History history;
...
}
Repository
public interface HistoryRepository
extends JpaRepository<History, Long>, RevisionRepository<History, Long, Integer> {
Optional<History> findByKeyEquals(final String key);
}
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
I have 2 entities that relate to one another. These 2 entities should map to each other in a Many-To-Many relationship, however, I need to also have a timestamp of their respective relationship (when it happened), so I am trying to map them using an intermediary table.
Initially, the relationship was One-To-Many, but I realized that I actually need a Many-To-Many as the business logic requires this. The structure is still the same, as in there is a Parent-Child relationship, but this time, a child should have multiple parents as well.
My BaseEntity is an abstract class that contains the fields present in all the other entities:
#Data
#MappedSuperclass
public abstract class BaseEntity {
#Id
#Min(100)
#Max(Integer.MAX_VALUE)
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
#CreationTimestamp
#Column(name = "Created_At", updatable = false)
protected ZonedDateTime createdDate;
#UpdateTimestamp
#Column(name = "Updated_At")
protected ZonedDateTime updatedDate;
#NotNull
#Column(name = "Is_Active")
protected Boolean active = true;
}
Then I have my 2 entities that should relate in a Many-To-Many style. This is my first entity and should be the parent:
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "User")
#EqualsAndHashCode(callSuper = true)
#TypeDefs( {
#TypeDef(name = "json", typeClass = JsonStringType.class),
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
public class UserEntity extends BaseEntity {
#NotBlank
#Column(name = "User_Name", columnDefinition = "varchar(255) default 'N/A'")
private String userName;
#Nullable
#JoinColumn(name = "User_Id")
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<UserRole> roleList = new ArrayList<>();
}
My second entity is considered the child entity:
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "Role")
#Where(clause = "is_active = true")
#EqualsAndHashCode(callSuper = true)
public class RoleEntity extends BaseEntity {
#NotBlank
#Column(name = "Name")
private String name;
#JsonIgnore
#JoinColumn(name = "Role_Id")
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<UserRole> userList = new ArrayList<>();
}
I also have my intermediary entity:
#Data
#Entity
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Where(clause = "is_active = true")
#EqualsAndHashCode(callSuper = true)
#Table(name = "User_Role", uniqueConstraints= #UniqueConstraint(columnNames={"User_Id", "Role_Id"}))
public class UserRole extends BaseEntity {
// Adding #JsonIgnore here will only cause an error
#JoinColumn(name = "User_Id")
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false, targetEntity = UserEntity.class)
private UserEntity user;
#JoinColumn(name = "Role_Id")
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false, targetEntity = RoleEntity.class)
private RoleEntity role;
}
Problem now is that when I try to get my UserEntity, I get infinite recursion.
So far I've tried using #JsonIgnore, #JsonManagedReference, #JsonBackReference and it did not work or I simply don't know where or how to use them properly.
Recap:
2 entities mapped by Many-To-Many relationship;
Many-To-Many implemented using an intermediary entity and One-To-Many + Many-To-One associations;
Getting recursion when showing my UserEntity;
Update: I managed to get this fixed using a different approach described in my answer to this question.
I fixed this by implementing a Composite Key structure and just using the #JsonIgnore annotation:
#Getter
#Setter
#Embeddable
#EqualsAndHashCode
#NoArgsConstructor
#AllArgsConstructor
public class UserRoleKey implements Serializable {
#Column(name = "User_Id")
Long userId;
#Column(name = "Role_Id")
Long roleId;
}
This gets to be used in the intermediary entity, which now doesn't use my BaseEntity anymore.
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "User_Role", uniqueConstraints= #UniqueConstraint(columnNames={"User_Id", "Role_Id"}))
public class UserRole {
#JsonIgnore
#EmbeddedId
private UserRoleKey id;
#JsonIgnore
#MapsId("userId")
#JoinColumn(name = "User_Id")
#ManyToOne(optional = false, targetEntity = UserEntity.class)
private UserEntity user;
#MapsId("roleId")
#JoinColumn(name = "Role_Id")
#ManyToOne(optional = false, targetEntity = RoleEntity.class)
private RoleEntity role;
#CreationTimestamp
#Column(name = "Created_At", updatable = false)
private ZonedDateTime createdDate;
}
Now, for my two entities, I have this definition:
UserEntity class (definition of the role):
#Nullable
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = true)
private List<UserRole> roleList = new ArrayList<>();
RoleEntity class (definition of the user)
#Nullable
#JsonIgnore
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "role", orphanRemoval = true)
private List<UserRole> userList = new ArrayList<>();
This seems to be working and no longer returns an infinite JSON recursion.
Hi I am trying below Entity classes for JPA Operations . I am getting an error Repeated column in mapping for entity #OneToOne should be mapped with insert="false" update="false" for column LOAN_ID . I tried some options and tried to follow some suggestions in other posts but nothing seems work , every try its giving some error . What is the mistake i am doing .
LOAN_UNDERWRITING - PK is LOAN_ID
LOAN_UNDERWRITING_STATUS - PK is LOAN_ID and UWS_ADD_DATE
Loans - PK is LOAN_ID
Root Entity Class
public class Loans {
#OneToOne(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
#JoinColumn(name = "LOAN_ID")
private LoanUnderWriting loanUnderWriting;
}
LOAN_UNDERWRITING - Entity Class
#DynamicUpdate
#Data
#AllArgsConstructor
#NoArgsConstructor
#ToString
#Table(name = "LOAN_UNDERWRITING")
#Entity
public class LoanUnderWriting {
#Id
#Column(name = "LOAN_ID")
private Long loanId;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "UW_ADD_DATE"),
#JoinColumn(name = "LOAN_ID") })
private LoanUnderWritingStatus loanUnderWritingStatus;
}
LOAN_UNDERWRITING_STATUS-Entity Class
#Data
#AllArgsConstructor
#NoArgsConstructor
#ToString
#Table(name = "LOAN_UNDERWRITING_STATUS")
#Entity
public class LoanUnderWritingStatus {
#EmbeddedId
private LoanUnderWritingStatusId loanUnderWritingStatusId;
}
LoanUnderWritingStatusId Class
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#ToString
#Embeddable
public class LoanUnderWritingStatusId implements Serializable{
private static final long serialVersionUID = 1L;
#Column(name = "LOAN_ID")
private Long loanId;
#Column(name = "UWS_ADD_DATE")
private String uwsAddDate;
}
Your mapping should have insertable = false, updatable = false in LoanUnderWriting Entity for LoanUnderWritingStatus as showun below.
#DynamicUpdate
#Data
#AllArgsConstructor
#NoArgsConstructor
#ToString
#Table(name = "LOAN_UNDERWRITING")
#Entity
public class LoanUnderWriting {
#Id
#Column(name = "LOAN_ID")
private Long loanId;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "UW_ADD_DATE", referencedColumnName = "UW_ADD_DATE", insertable = false, updatable = false),
#JoinColumn(name = "LOAN_ID", referencedColumnName = "LOAN_ID", insertable = false, updatable = false) })
private LoanUnderWritingStatus loanUnderWritingStatus;
}
In my Spring Boot project I have an entity class User
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Builder
#Entity
#Table(name = "applicationusers")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
[...]
#Singular
#ManyToMany(
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
#JoinTable(
name = "applicationusers_roles",
joinColumns = { #JoinColumn(name = "applicationuser_id") },
inverseJoinColumns = { #JoinColumn(name = "role_id") }
)
private Set<Role> roles;
}
and a second entity class role
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Builder
#Entity
#Table(name = "roles")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false, unique = true)
#Length(min = 3, max = 15)
private String name;
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "roles")
#Fetch(value = FetchMode.SUBSELECT)
#Singular
private Set<User> applicationUsers;
}
In one of my tests, I try to store a Role with a User to the repository like this
#Test
public void createRoleWithUsers() {
User newUser = User.builder()
.name("name")
.password("1234567")
.email("hello#world.net")
.enabled(true)
.build();
User savedUser = userRepository.save(newUser);
Set<User> users = new HashSet<>();
users.add(savedUser);
Role role = Role.builder()
.name("TestRole")
.applicationUsers(users)
.build();
Role createdRole = roleRepository.save(role);
Role foundRole = roleRepository.findRoleById(createdRole.getId()).get();
[...]
}
Debugging this code, I found out, that createdRole has the users set as expected but foundRole doesn't.
How can I get the users in foundRole too?
Please tell me if you need the repository-code - it's a very simple interface so I just skipped it.
Simplified response, read the fine manual: 6.3.10. Configuring Fetch- and LoadGraphs.
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Builder
#Entity
#Table(name = "applicationusers")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Singular
#ManyToMany
private Set<Role> roles;
}
and
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Builder
#Entity
#Table(name = "roles")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(mappedBy = "roles")
#Singular
private Set<User> applicationUsers;
}
The manual specifies:
public interface RoleRepository extends JpaRepository<Role, Long>{
#Query("select r from Role r where r.id = :id")
#EntityGraph(attributePaths = {"applicationUsers"})
Role findByIdFetchUsers(#Param("id") Long id);
}
The second System.out.println will give you a org.hibernate.LazyInitializationException.
#Override
public void run(String... args) throws Exception {
User u = save();
Role r2 = roleRepository.findByIdFetchUsers(u.getId());
System.out.println("R: " + r2 + " : " + r2.getApplicationUsers());
Role r1 = roleRepository.findById(u.getId()).get();
System.out.println("R: " + r1 + " : " + r1.getApplicationUsers());
}