Hibernate - map a row to either of two subclasses - spring

I have a Superclass
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "entity_type", discriminatorType = DiscriminatorType.STRING)
#Table(name = "my_super")
public abstract class MySuper{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "super_id")
private Long id;
}
and two subclasses
#Entity
#DiscriminatorValue("sub1")
public class Sub1 extends MySuper {}
#Entity
#DiscriminatorValue("sub2")
public class Sub2 extends MySuper {}
Now if another class has both of these subclasses in it, is it possible to instantiate one of them through the same join table - same row ??
For instance:
#Entity
#Table(name = "caller")
public class Caller{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "caller_id")
Long id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "caller_super", joinColumns = #JoinColumn(name = "caller_id"), inverseJoinColumns = #JoinColumn(name = "super_id"))
private Set<Sub1> sub1s;
#ManyToOne(fetch = FetchType.LAZY)
#JoinTable(name = "caller_super", joinColumns = #JoinColumn(name = "caller_id"), inverseJoinColumns = #JoinColumn(name = "super_id"))
Sub2 sub2;
}
I keep getting this error when trying to instantiate a Caller Object:
org.springframework.orm.hibernate3.HibernateSystemException: could not set a field value by reflection setter of my.test.Caller.sub2; nested exception is org.hibernate.PropertyAccessException: could not set a field value by reflection setter of my.test.Caller.sub2
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:679)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:102)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:368)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
nested exception is org.hibernate.PropertyAccessException: could not set a field value by reflection setter

i think this two line shouldnt be same
#JoinTable(name = "caller_super", joinColumns = #JoinColumn(name = "caller_id"), inverseJoinColumns = #JoinColumn(name = "super_id"))
private Set<Sub1> sub1s;
--
#JoinTable(name = "caller_super", joinColumns = #JoinColumn(name = "caller_id"), inverseJoinColumns = #JoinColumn(name = "super_id"))
private Sub2 sub2;
because they are not same entity and their join table should be different. For ManyToMany relationship type is Sub1 but if you try to put them same table hibernate will try to put Sub2 to sub1s . But it is not vaild. Try to change your join table. For ManyToOne relationship.

Related

#ManyToMany SpringBoot JSON 415 error can`t post to table or can`t get list(n>=1) because of infinite loop caused by relationship

I`ve been trying to create a #ManyToMany relationship between two entities (team&contest) but when i try to post to the contest controller api i get a error 415 saying that
Failed to evaluate Jackson deserialization for type [[simple type, class com.project.Contest.Contest]]: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot handle managed/back reference 'defaultReference': back reference type (`java.util.List<com.project.Contest.Contest>`) not compatible with managed type (com.project.Contest.Contest)
team :
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "team")
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#JsonBackReference
//#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class , property = "id")
#ManyToMany(mappedBy = "teams", cascade = CascadeType.PERSIST)
private List<Contest> contests;
private String name;
private int wins, losses;
}
contest :
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "contest")
public class Contest {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#ManyToMany(cascade = CascadeType.PERSIST)
#JsonManagedReference
//#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class , property = "id")
#JoinTable(
name = "team_contest",
inverseJoinColumns = #JoinColumn(name = "team_id"),
joinColumns = #JoinColumn( name = "contest_id")
)
private List<Team> teams;
#ManyToMany(cascade = CascadeType.PERSIST)
#JsonManagedReference
//#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class , property = "id")
#JoinTable(
name = "contest_user",
joinColumns = #JoinColumn(name = "contest_id"),
inverseJoinColumns = #JoinColumn( name = "user_id")
)
private List<User> users;
}
i found out i can use #JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class , property = "id") instead of #JsonBackReference & #JsonManagedReference which helped me as it let me post to the database but then i refaced the problem that i can`t retrieve contest.teams[1] as because both objects have references to one another it creates some kind of an infinite loop as to get to the reference of the second object(contest.teams[1]) it needs to show the reference the contest.teams[0] has to the contest and soo forth. please help <3
This is the most famous bi-directional issue. To break up the loop while serialization, you can choose:
#JsonIgnore
#JsonIdentityInfo
JPA Projections
#EntityGraph
Or simply make the relationship uni-directional
thanks meridbt i was already using #JsonIdentityInfo but in the wrong place and i read a bit online and fixed my issue by doing this :
team :
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "team")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class , property = "id")
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToMany(mappedBy = "teams", cascade = CascadeType.PERSIST)
private List<Contest> contests;
private String name;
private int wins, losses;
}
contest :
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "contest")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Contest {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#ManyToMany(cascade = CascadeType.PERSIST)
#JoinTable(
name = "team_contest",
inverseJoinColumns = #JoinColumn(name = "team_id"),
joinColumns = #JoinColumn(name = "contest_id")
)
private List<Team> teams;
#ManyToMany(cascade = CascadeType.PERSIST)
#JoinTable(
name = "contest_user",
joinColumns = #JoinColumn(name = "contest_id"),
inverseJoinColumns = #JoinColumn(name = "user_id")
)
private List<User> users;
}

OSIV:false , cannot get session for resolving FetchType.Lazy items, even in the scope of #Transactional

In my spring application, I have disabled osiv (spring.jpa.open-in-view=false) to avoid holding hibernate sessions throughout a request lifecycle.
After disabling OSIV, I am getting the following error when I try to access a field in my entity that has the FetchType set as LAZY
org.hibernate.LazyInitializationException: could not initialize proxy [sg.identity.auth.entity.OrganizationEntity#2] - no Session
This is the entity I'm using and I'm trying to access userEntity.getOrganization() which results in the LazyInitializationException.
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "\"USER\"")
public class UserEntity extends AbstractAuditingEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long userId;
#OneToOne(fetch = FetchType.LAZY)
#JoinTable(name = "organization_user",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "user_id"),
inverseJoinColumns = #JoinColumn(name = "organization_id", referencedColumnName = "organization_id"))
private OrganizationEntity organization;
#ManyToMany(cascade = CascadeType.MERGE)
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JoinTable(name = "user_parent_organization_unit_mapping",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "user_id"),
inverseJoinColumns = #JoinColumn(name = "organization_unit_id", referencedColumnName = "organization_unit_id"))
private Set<OrganizationUnitEntity> organizationUnits;
#OneToOne(cascade = CascadeType.ALL)
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JoinTable(name = "user_exclusion_mapping",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "user_id"),
inverseJoinColumns = #JoinColumn(name = "excluding_permission_set_id", referencedColumnName = "permission_set_id"))
private PermissionSetEntity exclusionPermissions;
#ManyToMany(cascade = CascadeType.MERGE)
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JoinTable(name = "user_role",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "role_id"))
private Set<RoleEntity> roles;
}
I'm calling the user.getOrganizationEntity in a method annotated with #Transactional so It should receive an open session to resolve the lazy items. But in my case, it is not working
#Transactional
public Organization getOrganization(String emailId) {
return userRepository.getUserByEmailId(emailId)
.map(UserEntity::getOrganization)
.map(OrganizationMapper::toDto)
.orElseThrow(() -> new IllegalArgumentException("error"));
}
The user.getOrganization is resolving correctly when I try using TransactionTemplate.
#Transactional
public Organization getOrganization(String emailId) {
return transactionTemplate.execute(status -> {
return userRepository.getUserByEmailId(emailId)
.map(UserEntity::getOrganization)
.map(OrganizationMapper::toDto)
.orElseThrow(() -> new IllegalArgumentException("error"));
});
}
I got it working with #Transactional itself by setting the property spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true, but from my research, i found that it is not recommended to set this flag as true

How to link two tables by third?

I have three tables:
1) book: id (primary), name
2) shop: code (unique, not primary), name
3) book_shop: book_id, shop_id (code), price
I want to get shops in book like
book.getShop();
How to link this entities?
I tried:
#Data
#NoArgsConstructor
#Entity
#Table(name = "book", schema = "example")
#EntityListeners(AuditingEntityListener.class)
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
#OneToMany(mappedBy = "book", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<BookShop> bookShop;
}
.
#Data
#NoArgsConstructor
#Entity
#Table(name = "shop", schema = "example")
#EntityListeners(AuditingEntityListener.class)
public class Shop {
#Id
private int code;
private String name;
#OneToMany(mappedBy = "shop", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<BookShop> bookShop;
}
.
#Data
#NoArgsConstructor
#Entity
#Table(name = "book_shop", schema = "example")
public class BookShop implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Id
#ManyToOne
#JoinColumn(name = "book_id")
private Book book;
#Id
#ManyToOne
#JoinColumn(name = "shop_id")
private Shop shop;
#Column(name = "price")
private int fromDate;
}
This code return empty set: Book book = bookRepostiory.getById(1).get().getBookShop()
Try the many to many mapping implement like as below remove your book_shop table,
add this code to shop entity,
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
#JoinTable(name = "book_shop",
joinColumns = {#JoinColumn(name = "book_id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "shop_id", nullable = false)})
private Set<Book> bookList = null;
add this code to book entity,
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL,
mappedBy ="bookList")
private Set<Shop> shopList=null;
if any issue inform!!
I would suggest, first - initialize the set in the entity
private Set<BookShop> bookShop = new HashSet<>();
Second, add fetch = FetchType.EAGER to your association, for e.g.
#OneToMany(fetch = FetchType.EAGER, mappedBy = "book", cascade = CascadeType.ALL)

Encountered with JPA ManytoMany Relationship Build time errors with IntelliJ IDEA

I'm very new to this topic so i followed a tutorial. after following steps i got some build time errors.
I have imported javax persistence like this.
import javax.persistence.*;
Then the student model class
#Entity
#Table(name="STUDENT")
public class Student {
#Id
#GeneratedValue
private Integer studentId;
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(name="Enrollment", joinColumns = {#JoinColumns(name="student_id")},
inverseJoinColumns = {#JoinColumns(name="course_id")})
private List<Course> courses = new ArrayList<>();
}
The Course model class.
#Entity
#Table(name="COURSE")
public class Course {
#GeneratedValue
private Integer id;
#ManyToMany(mappedBy ="courses")
private List<Student> students = new ArrayList<>();
These are set of errors that i have got
incompatible types: javax.persistence.JoinColumns cannot be converted to javax.persistence.JoinColumn
cannot find symbol
symbol: method name()
location: #interface javax.persistence.JoinColumns
annotation #javax.persistence.JoinColumns is missing a default value for the element 'value'
Can anyone help me to get rid of this issues?
Thanks.
A #JoinTable annotation really has a joinColumns parameter, but the syntax you had used is not correct. If tables are joined by only column and inverse column you shouldn't use a #JoinColumns annotation. You have to change it in the following way:
#Entity
#Table(name="STUDENT")
public class Student {
#Id
#GeneratedValue
private Integer studentId;
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="Enrollment",
joinColumns = #JoinColumn(name="student_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name="course_id", referencedColumnName = "id"))
private List<Course> courses = new ArrayList<>();
}
and only if joining goes by more then one column you have to do something like this:
#Entity
#Table(name="STUDENT")
public class Student {
#Id
#GeneratedValue
private Integer studentId;
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="Enrollment",
joinColumns = #JoinColumns{
#JoinColumn(name="student_id", referencedColumnName = "id"),
#JoinColumn(name="another_id", referencedColumnName = "another_id")
},
inverseJoinColumns = #JoinColumn(name="course_id", referencedColumnName = "id"))
private List<Course> courses = new ArrayList<>();
}
Hope it will help

How to create Predicate BooleanExpression for many to many relations in QueryDSL

How can inner join be done on Many to Many relations using Predicate BooleanExpression?
I have 2 entities
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#ManyToMany(fetch = FetchType.LAZY,
cascade = { CascadeType.DETACH, CascadeType.MERGE,
CascadeType.REFRESH, CascadeType.PERSIST})
#JoinTable(name = "a_b_maps",
joinColumns = #JoinColumn(name = "a_id", nullable =
false,referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "b_id", nullable = false,
referencedColumnName = "id")
)
private Set<B> listOfB = new HashSet<B>();
}
public class B {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#ManyToMany(fetch = FetchType.LAZY,
cascade = { CascadeType.DETACH, CascadeType.MERGE,
CascadeType.REFRESH, CascadeType.PERSIST})
#JoinTable(name = "a_b_maps",
joinColumns = #JoinColumn(name = "b_id", nullable =
false,referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "a_id", nullable = false,
referencedColumnName = "id")
)
private Set<A> listOfA = new HashSet<A>();
}
A Base repo
#NoRepositoryBean
public interface BaseRepository<E, I extends Serializable>
extends JpaRepository<E, I> {
}
And a repository class for A
public interface Arepo extends BaseRepository<A, Integer>,
QueryDslPredicateExecutor<A> {
Page<A> findAll(Predicate predicate, Pageable pageRequest);
}
Now I want to use A Repo with Predicate query. I need to form a predicate where I can load A based on some given Bs
I tried
QA a = QA.a;
QB b = QB.b;
BooleanExpression boolQuery = null;
JPQLQuery<A> query = new JPAQuery<A>();
query.from(a).innerJoin(a.listOfB, b)
.where(b.id.in(someList));
Now I am able to form a JPQLQuery, but the repository expects a Predicate. How can I get Predicate from the JPQLQuery??
Or, how can the inner join be achieved using Predicate?
I am able to create a Predicate with the help of answer given here
https://stackoverflow.com/a/23092294/1969412.
SO, instead of using JPQLQuery, I am directly using
a.listOfB.any()
.id.in(list);
This is working like a charm.

Resources