How can I fetch multiple lazy loaded OneToMany lists? - jpa

I have an Entity with multiple unidirectional OneToMany relationships like following.
How can I fetch all this fields in one query?
What would be the best way if I have up to 10 Arraylists with a OneToMany relationship?
#Data
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "members")
public class Member extends Auditable<String> {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Setter(AccessLevel.NONE)
private Long id;
#OneToOne
private Gender gender;
private String lastName;
private String firstName;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinColumn(name = "person_id")
private List<Phone> phoneList = new ArrayList<>();
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinColumn(name = "person_id")
private List<EMail> eMailList = new ArrayList<>();
// more Lists with OneToMany relationship
}
#Data
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "emails")
public class EMail extends Auditable<String> {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Setter(AccessLevel.NONE)
private Long id;
private Type type;
private String value;
}
I tried following step in my MemberRepository class which is ending in a MultipleBagFetchException:
#Query("SELECT m " +
"FROM Member m " +
"LEFT JOIN FETCH m.eMailList " +
"LEFT JOIN FETCH m.phoneList " +
"WHERE m.memberId = ?1")
Optional<Member> findByMemberIdWithAllInfoQuery(Long id); // MultipleBagFetchException
Then I tried following step with this information https://vladmihalcea.com/hibernate-multiplebagfetchexception/ which also doesn't work properly:
public Optional<Member> findMemberWithAllFieldsQuery(Long memberId) {
Member _member = entityManager.createQuery(
"SELECT DISTINCT m " +
"FROM Member m " +
"LEFT JOIN FETCH m.eMailList " +
"WHERE m.memberId = :id ", Member.class)
.setParameter("id", memberId)
.setHint(QueryHints.HINT_PASS_DISTINCT_THROUGH, false)
.getSingleResult();
_member = entityManager.createQuery(
"SELECT DISTINCT m " +
"FROM Member m " +
"LEFT JOIN FETCH m.phoneList " +
"WHERE m in :member ", Member.class)
.setParameter("member", _member)
.setHint(QueryHints.HINT_PASS_DISTINCT_THROUGH, false)
.getSingleResult();
return Optional.of(_member);
}
Thanks for your help/hints!

Try to use Set instead of List ? I've run into this exception and solved it after changing my collection to Set
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinColumn(name = "person_id")
private Set<Phone> phoneList = new HashSet<>();
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinColumn(name = "person_id")
private Set<EMail> eMailList = new HashSet<>();

Related

Using Named Entity Graphs in Eclipselink not work

I have 2 classes
Collections
#Entity
#NamedEntityGraph(
name = "cars-graph",
attributeNodes = #NamedAttributeNode("cars"))
public class Collections {
#Id
#ReturnInsert(returnOnly=true)
private Long id;
#ManyToMany(cascade = ALL)
#JoinTable(name="CarsCollections",
joinColumns= #JoinColumn(name="COLLECTIONS_ID", referencedColumnName="ID"),
inverseJoinColumns=
{#JoinColumn(name = "MODELSRANGE_ID", referencedColumnName = "MODELSRANGE_ID"),
#JoinColumn(name = "MODELS_ID", referencedColumnName = "MODELS_ID"),
#JoinColumn(name = "TYPES_ID", referencedColumnName = "TYPES_ID")
}
)
public List<Cars> cars;
and Cars
#Entity(name = "Cars")
public class Cars {
#EmbeddedId
private CarsId id;
#ManyToOne(fetch = FetchType.EAGER)
#MapsId("modelsRangeid")
#JoinColumn(name = "MODELSRANGE_ID")
private ModelsRange modelsrange;
#ManyToOne(fetch = FetchType.EAGER)
#MapsId("modelsid")
#JoinColumn(name = "MODELS_ID")
private Models models;
#ManyToOne(fetch = FetchType.EAGER)
#MapsId("typesid")
#JoinColumn(name = "TYPES_ID")
private Types types;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
#JoinColumn(name = "MAKE")
private Makes makes;
When i try to get Collections with Cars
EntityGraph entityGraph = em.getEntityGraph("cars-graph");
Collections collections = em.createQuery("select r from Collections r where r.id = :id", Collections.class)
.setParameter("id", 1L)
.setHint("javax.persistence.fetchgraph", entityGraph)
.getSingleResult();
System.out.println(collections.getCars());
I get {IndirectList: not instantiated}. Does not give errors, but Cars does not load.
If you add to the Collections entity for the Cars field "EAGER"
...
#ManyToMany(cascade = ALL, fetch = FetchType.EAGER)
#JoinTable(name="CarsCollections",
...
It works.
But then Cars will always load.
I use Jakarta EE9 and Eclipselink
Problem in javax name. In Jakarta EE need to use:
...
.setHint("jakarta.persistence.loadgraph", entityGraph)
...

JPA Join with childEntity.childForeignEntity

I do not know how it is called in one word, but let me explain in details.
Lets assume I have following tables/schema in my database:
And following classes accordingly:
1.Post
#Entity
#Table(name = "posts")
public class Post {
#Id
private Long id;
#Column(name = "text")
private String text;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "post")
private Set<PostComment> postComments = new HashSet<>();
}
2.Post Comments
#Entity
#Table(name = "post_comments")
public class PostComment {
#Id
private Long id;
#Column(name = "post_id")
private Long postId;
#Column(name = "user_id")
private Long userId;
#Column(name = "text")
private String text;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="post_id")
private Post post;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="user_id")
private User user;
}
3.User
#Entity
#Table(name = "users")
public class User {
#Id
private Long id;
#Column(name = "some_attributes")
private String someAttributes;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private Set<PostComment> postComments = new HashSet<>();
}
How can I join Post with User via PostComment, so in my Post entity I could get all users commented:
#Entity
#Table(name = "posts")
public class Post {
....
//# join with post_comments.user_id
private Set<User> users = new HashSet<>();
....
}
Well, just get PostComment.user where PostComment.post equals your post.
#Query("select pc.user from PostComment pc where pc.post = :post")
List<User> getUsersWithComments(#Param("post") Post post);
Seems to work for me. Gives me the following SQL:
Hibernate: select user1_.id as id1_2_, user1_.some_attributes as some_att2_2_ from post_comments postcommen0_ inner join users user1_ on postcommen0_.user_id=user1_.id where postcommen0_.post_id=?
I don't know what this is all about:
#Column(name = "post_id")
private Long postId;
#Column(name = "user_id")
private Long userId;
or this
#JoinColumn(name="user_id")
#JoinColumn(name="post_id")
and you shouldn't do this:
= new HashSet<>();
and while we're at it this is redundant.
fetch = FetchType.LAZY,

JPA Repository Query on additional table #ManytoMany

I want to do select like this in my jpa spring repository
SELECT sicknes_id, count(symptomp_id) as ilosc FROM symptomp_sicknes where symptomp_id IN (1,2) group by sicknes_id Order by ilosc DESC;
My enitity
#Entity
#Table(name = "symptomp")
public class Symptomp {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "symptomp_id")
private Long symptomp_id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#ManyToMany(cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH}, fetch = FetchType.LAZY)
#JoinTable(name = "symptomp_sicknes",joinColumns = #JoinColumn(name = "symptomp_id"),inverseJoinColumns = #JoinColumn(name = "sicknes_id"))
private Set<Sicknes> sicknes = new HashSet<>();
#Entity
#Table(name = "sicknes")
public class Sicknes {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sicknes_id")
private Long sicknes_id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#ManyToOne(cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH}, fetch = FetchType.LAZY)
#JoinColumn(name = "speciesId")
private Species species;
My Symptomp repository:
public interface SymptompRepository extends JpaRepository<Symptomp, Long> {
#Query("select p from Symptomp p where name like ?1%")
public List<Symptomp> findAllBySymptompName(String symptomp);
public Symptomp findByName(String symptomp);
public List<Symptomp> findByNameIn(List<String> list);
Integer countDistinctSymptompByName(String id);
}
How I can create this select in my JPA repository?
I try get value like in select but i got error mapping bean.
You can get query result as List<Object[]> using nativeQuery=true parameter
#Query("SELECT sicknes_id, count(symptomp_id) as ilosc FROM symptomp_sicknes where symptomp_id IN (1,2) group by sicknes_id Order by ilosc DESC", nativeQuery=true)
List<Object[]> getQueryResult();
Other option is to create dto class with appropriate constructor
public class QueryResultDto {
Long sicknesId;
Long count;
public QueryResultDto(Long sicknesId, Long count) {
this.sicknesId = sicknesId;
this.count = count;
}
}
Then using JPQL
#Query("select new yourproject.dtopath.QueryResultDto(...")
List<QueryResultDto> getQueryResult(#Param("symptompIds") List<Long> symptompIds);
If you want to avoid a native Query the best way is to create an Entity for that JoinTable. Then you can query it easily. Additional benefit if this is that if in future a requirement will pop up that you have to store additional attributes in that relation you will have the Entity already there to do that easily.

JPA query with left join and "not exists"

I am writing a JPA query using TopLink which involves the following three entities.
#Entity
#Table(name = "OFFERS")
public class Offers implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="offers_seq_gen")
#SequenceGenerator(name="offers_seq_gen", sequenceName="OFFERS_SEQ")
#Basic(optional = false)
#Column(name = "OFFERID")
private Long offerid;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "offers", fetch = FetchType.LAZY)
private List<Coupons> couponsList;
}
#Entity
#Table(name = "COUPONS")
public class Coupons implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="coupons_seq_gen")
#SequenceGenerator(name="coupons_seq_gen", sequenceName="COUPONS_SEQ")
#Basic(optional = false)
#Column(name = "COUPONID")
private Long couponid;
#Basic(optional = false)
#Column(name = "ISSUED", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
#Temporal(TemporalType.TIMESTAMP)
private Date issued;
#JoinColumn(name = "USERID", referencedColumnName = "USERID")
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Users users;
#JoinColumn(name = "OFFERID", referencedColumnName = "OFFERID")
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Offers offers;
#Entity
#Table(name = "USERS")
public class Users implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="users_seq_gen")
#SequenceGenerator(name="users_seq_gen", sequenceName="USERS_SEQ")
#Basic(optional = false)
#Column(name = "USERID")
private Long userid;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "users", fetch = FetchType.LAZY)
private List<Coupons> couponsList;
I need to find all the Offers who either have no coupons for a given user or all the coupons for the user were issued more than a day ago.
I have tried many different approaches and the only query I have come up with so far, which does not crash the server on deployment is:
SELECT o
FROM Offers o
LEFT JOIN o.couponsList c
WHERE
c.users.userid = :userid AND c.issued < :yesterday
OR
NOT EXISTS
(SELECT c1
FROM Coupons c1
WHERE c1.offers = o AND c1.users.userid = :userid)
But it does not return the Offer when the Coupons entry does not exist.
I managed to find a working query. Leaving it here for reference if anyone had similar issues:
SELECT o FROM Offers o WHERE
NOT EXISTS
(SELECT c FROM Coupons c WHERE c.users.userid = :userid
AND c.issued > :yesterday AND c.offers = o)
OR NOT EXISTS
(SELECT c1 FROM Coupons c1 WHERE c1.offers = o
AND c1.users.userid = :userid)

Netbeans wizard Entity Classes from Database, not all tables mapped

I used this wizard to create entity classes from my database. Some tables have not been transformed into classes, but there are attributes that identify the relationships.
this is my db ERD (mysql)
and this is the user entity class (attributes)
#Entity
#Table(name = "user")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "User.findAll", query = "SELECT u FROM User u"),
#NamedQuery(name = "User.findByOid", query = "SELECT u FROM User u WHERE u.oid = :oid"),
#NamedQuery(name = "User.findByUsername", query = "SELECT u FROM User u WHERE u.username = :username"),
#NamedQuery(name = "User.findByPassword", query = "SELECT u FROM User u WHERE u.password = :password"),
#NamedQuery(name = "User.findByEmail", query = "SELECT u FROM User u WHERE u.email = :email"),
#NamedQuery(name = "User.findByAddress", query = "SELECT u FROM User u WHERE u.address = :address"),
#NamedQuery(name = "User.findBySince", query = "SELECT u FROM User u WHERE u.since = :since")})
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "oid")
private Integer oid;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 15)
#Column(name = "username")
private String username;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 15)
#Column(name = "password")
private String password;
// #Pattern(regexp="[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message="Invalid email")//if the field contains email address consider using this annotation to enforce field validation
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 30)
#Column(name = "email")
private String email;
#Size(max = 50)
#Column(name = "address")
private String address;
#Basic(optional = false)
#NotNull
#Column(name = "since")
#Temporal(TemporalType.DATE)
private Date since;
#JoinTable(name = "favorite", joinColumns = {
#JoinColumn(name = "user_oid", referencedColumnName = "oid")}, inverseJoinColumns = {
#JoinColumn(name = "wheelchair_oid", referencedColumnName = "oid")})
#ManyToMany
private List<Wheelchair> wheelchairList;
#ManyToMany(mappedBy = "userList1")
private List<Wheelchair> wheelchairList1;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "senderOid")
private List<Comment> commentList;
#JoinColumn(name = "role_oid", referencedColumnName = "oid")
#ManyToOne(optional = false)
private Role roleOid;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "userOid")
private List<Orthopedy> orthopedyList;
public User() {
}
...
i can't understand something:
where is the OWN join table?
why i have userList1 and wheelchairList1? should it identifies OWN table? in this case i can rename it here or i have to rename it in some xml file?
why of
#OneToMany(cascade = CascadeType.ALL, mappedBy = "userOid")
private List<Orthopedy> orthopedyList;
?
it should be OneToOne...
moreover the "JSF from entities class" wizard creates CRUD operation to manage Users, how can i manage join tables? I need to write something in the controller like what?
can you please link me some resource where i can learn this?
thank you so much
While Creating Entities It Creates Classes For All Tables With Primary Key
But not for tables that have many to many relations . its managed by their parent classes it is maintained as a list.
This is my code for managing my many to many table of SubjectFaculty which has details of Faculty and Subjects
Assigning A Subject To Faculty
public void assignFacultyToSubject(String facultyUname, Integer subjectId) {
try {
Subject oSubject = em.find(Subject.class, subjectId);
Faculty oFaculty = em.find(Faculty.class, facultyUname);
College oCollege = em.find(College.class, oFaculty.getCollegeUname().getCollegeUname());
List<Faculty> lstFaculty = oSubject.getFacultyList();
List<Subject> lstSubject = oFaculty.getSubjectList();
if (!lstSubject.contains(oSubject)) {
lstFaculty.add(oFaculty);
lstSubject.add(oSubject);
oSubject.setFacultyList(lstFaculty);
oFaculty.setSubjectList(lstSubject);
em.merge(oSubject);
em.getEntityManagerFactory().getCache().evictAll();
} else {
System.out.println("Entry Already Found");
}
} catch (Exception e) {
System.out.println("Error :- " + e.getMessage());
}
}
Removing Subject And Faculty Details Form Many to Many Table
#Override
public void removeFacultySubject(String facultyUname, Integer subjectId) {
try {
Subject oSubject = em.find(Subject.class, subjectId);
Faculty oFaculty = em.find(Faculty.class, facultyUname);
List<Subject> lstSubject = oFaculty.getSubjectList();
List<Faculty> lsFaculty = oSubject.getFacultyList();
lstSubject.remove(oSubject);
lsFaculty.remove(oFaculty);
em.merge(oSubject);
} catch (Exception e) {
System.out.println("Error :- " + e.getMessage());
}
}