Hibernate JPA #OneToMany join on 2 columns with OR statement in Entity - java

I have the following class:
public class Nomenclature extends BaseEntity {
#Id
#Column(name = "NOMENCLATURE_CODE")
private String nomenclatureCode;
#OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL}, mappedBy = "nomenclatureCode", orphanRemoval = true)
private List<Cumul> cumuls = new ArrayList<>();
}
I want to join the following Cumul class on 2 columns WHERE cumul.nomenclatureCode = nomenclature.nomenclatureCode OR cumul.nomenclatureCodeAllowedCumul = nomenclature.nomenclatureCode
public class Cumul extends BaseEntity {
#Id
#Column(columnDefinition = "NUMERIC")
#GeneratedValue(generator = "CUMUL", strategy = GenerationType.SEQUENCE)
private Long id;
#Column(name = "NOMENCLATURE_CODE")
private String nomenclatureCode;
#Column(name = "NOMENCLATURE_CODE_ALLOWED_CUMUL")
private String nomenclatureCodeAllowedCumul;
#OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL},orphanRemoval = true)
#JoinColumns(
{
#JoinColumn(name = "nomenclature_code", referencedColumnName = "nomenclature_code"),
#JoinColumn(name = "nomenclature_code_allowed_cumul", referencedColumnName = "nomenclature_code")
})
private List<Cumul> cumuls = new ArrayList<>();
Worked but this an AND statement not an OR what I was looking for

Related

How to replace CriteriaBuilder with Spring JPA

I have next classes:
#Entity
#Table
public class Lesson implements ModelEntity {
#Id
#Column(name = "lesson_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "course_id")
private Course course;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "lesson_type_id")
private LessonType lessonType;
private LocalDate date;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "time_slot_id")
private TimeSlot timeSlot;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "auditorium_id")
private Auditorium auditorium;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "lesson_teacher", joinColumns = #JoinColumn(name = "lesson_id"), inverseJoinColumns = #JoinColumn(name = "person_id"))
private Set<Teacher> teachers = new HashSet<>();;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "lesson_group", joinColumns = #JoinColumn(name = "lesson_id"), inverseJoinColumns = #JoinColumn(name = "group_id"))
private Set<Group> groups = new HashSet<>();
}
#Entity
#Table(name = "groups")
public class Group implements ModelEntity {
#Id
#Column(name = "group_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "group_name")
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "faculty_id")
private Faculty faculty;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "group")
private List<Student> students;
}
#Entity
#Table
public class TimeSlot implements ModelEntity {
#Id
#Column(name = "time_slot_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "time_slot_number")
private Integer number;
#Column(name = "time_slot_name")
private String name;
#Column(name = "time_slot_start")
private LocalTime startTime;
#Column(name = "time_slot_end")
private LocalTime endTime;
}
I wrote method, that find all Groups_id by Date and TimeSlot_id not connected to Lesson with CriteriaBuilder API, it works perfect:
#Override
public Set<Integer> getBusyGroupsId(int lessonId, LocalDate date, int timeSlotId) {
logger.debug("getBusyGroupsId() with agruments {}, {}, {}.", lessonId, date, timeSlotId);
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Integer> query = criteriaBuilder.createQuery(Integer.class);
Root<Lesson> root = query.from(Lesson.class);
List<Predicate> predicates = new ArrayList<>();
Join<Lesson, TimeSlot> timeSlotJoin = root.join("timeSlot", JoinType.LEFT);
predicates.add(criteriaBuilder.equal(timeSlotJoin.get("id"), timeSlotId));
predicates.add(criteriaBuilder.equal(root.get("date"), date));
if (nonNull(lessonId)) {
predicates.add(criteriaBuilder.notEqual(root.get("id"), lessonId));
}
query.where(predicates.toArray(new Predicate[] {}));
SetJoin<Lesson, Group> joinGroup = root.joinSet("groups");
query.multiselect(joinGroup.get("id"));
TypedQuery<Integer> result = entityManager.createQuery(query);
return result.getResultStream().collect(Collectors.toSet());
}
But after that I think- what about JPA, can it be easier?
I tried something like that, but it doesnt work:
public Set<Integer> findGroupIdByIdNotAndDateEqualsAndTimeSlotIdEquals(Integer lessonId, LocalDate date, Integer timeSlotId);
How to fix it?
Also I stacked with writing method with JPA that should find all Lesson by Group_id and Date(or startDate-endDate) and sort it: first by date, second- by TimeSlot_number.
Can it be written with JPA?
Thanks in advance.
Don't throw stones, I'm just getting to know Spring JPA.

Hibernate OneToMany mapping & Query generation : More than one row with the given identifier was found

I am using spring-boot-starter-data-jpa 1.5.1.RELEASE which internally uses hibernate-core 5.0.11.Final
My entity looks like this:
AreaDto
#Entity
#Table(name = "AREA")
#EntityListeners(AuditingEntityListener.class)
public class AreaDto {
#Id
#Column(name = "AREA_ROWID")
private String areaRowId;
#OneToMany(cascade = CascadeType.DETACH)
#JoinColumn(name = "AREA_ROWID")
private Collection<FestivalDto> festival;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<ActionDto> actions;
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(name = "FESTIVAL", joinColumns = {
#JoinColumn(name = "AREA_ROWID", referencedColumnName = "AREA_ROWID")}, inverseJoinColumns = {
#JoinColumn(name = "FESTIVAL_ROWID", referencedColumnName = "FESTIVAL_ROWID")})
private Collection<ActionDto> festivalActions;
}
FestivalDto
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "FESTIVAL")
public class FestivalDto {
#Id
#Column(name = "FESTIVAL_ROWID")
#GeneratedValue(generator = "FESTIVAL_ROWID_SEQ")
private Long festivalRowId;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "festival")
private Collection<ActionDto> actions = Lists.newArrayList();
}
ActionDto
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "ACTION")
public class ActionDto implements Serializable {
...
#Id
#Column(name = "ACTION_ID")
#GeneratedValue(generator = "ACTION_ID_SEQ")
private Long actionId;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "FESTIVAL_ROWID")
private FestivalDto festival;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
}
I'm trying to make sense of the below ideas:
What is the strategy used by hibernate to decide on the festival_rowid (or festival_row ids) used to get all the associated action? How will hibernate generated SQL query vary if i change festivalActions fetch strategies between LAZY and EAGER? I know about proxying, collection proxying and all, my question is specific to how those sql is generated and how it may have an impact on deciding the value of bind parameter.
Is my mapping accurate or should I be using a multimap for this relationship since an area could have multiple festival and each festival could have multiple actions
Background:
I am getting below error which goes away if I change the fetch type from LAZY to EAGER. Hoping to understand the behaviour for gaining some confidence in the fix. I have read SO and error
org.hibernate.HibernateException: More than one row with the given identifier was found: data.dto.ActionDto#280856b5
This mapping does not make much sense. You can't map festivalActions this way because there is no way to persist the state properly through such a mapping. Also festival in AreaDto should be mapped by the area in FestivalDto. Try the following instead:
#Entity
#Table(name = "AREA")
#EntityListeners(AuditingEntityListener.class)
public class AreaDto {
#Id
#Column(name = "AREA_ROWID")
private String areaRowId;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<FestivalDto> festival;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<ActionDto> actions;
public Collection<ActionDto> getFestivalActions() {
return festival.stream().flatMap(f -> f.actions.stream()).collect(Collectors.toList());
}
}
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "FESTIVAL")
public class FestivalDto {
#Id
#Column(name = "FESTIVAL_ROWID")
#GeneratedValue(generator = "FESTIVAL_ROWID_SEQ")
private Long festivalRowId;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "festival")
private Collection<ActionDto> actions = Lists.newArrayList();
}
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "ACTION")
public class ActionDto implements Serializable {
...
#Id
#Column(name = "ACTION_ID")
#GeneratedValue(generator = "ACTION_ID_SEQ")
private Long actionId;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "FESTIVAL_ROWID")
private FestivalDto festival;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
}

Foreign key must have same number of columns as the referenced primary key. But I'm not using a composite key

I have a LibraryModel class, a LibraryImage class and a LibraryAttribute class. A LibraryModel can have an arbitrary number of LibraryImages and LibraryAttributes.
The error that I get:
org.hibernate.MappingException: Foreign key (FKmbn4xh7xdxv371ao5verqueu3:library_item_attribute [LIBRARY_ITEM_ATTRIBUTE_ID])) must have same number of columns as the referenced primary key (library_item_attribute [LIBRARY_ITEM_ID,LIBRARY_ITEM_ATTRIBUTE_ID])
Here are my annotated Objects:
Library Model:
#Entity
#Table(name = "library_item", uniqueConstraints = {
})
#Inheritance(strategy = InheritanceType.JOINED)
public class LibraryItemModel implements LibraryItem{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "LIBRARY_ITEM_ID", unique = true, nullable = false)
private Integer libraryItemId;
#Column(name = "ITEM_TITLE", unique = false, nullable = false)
private String itemTitle;
#Column(name = "IS_PARENT", nullable = false)
private Boolean isParent;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="LIBRARY_ID", nullable = false)
private LibraryModel libraryModel;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "ITEM_LISTING",
joinColumns = {#JoinColumn(name = "PARENT_LIB_ITEM_ID", nullable=false)},
inverseJoinColumns = {#JoinColumn(name="CHILD_LIB_ITEM_ID", nullable = false)})
private Set<LibraryItemModel> itemChildren = new HashSet<>();
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "itemChildren")
private Set<LibraryItemModel> itemParents = new HashSet<>();
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "LIBRARY_ITEM_IMAGE",
joinColumns = { #JoinColumn(name = "LIBRARY_ITEM_ID", nullable=false)},
inverseJoinColumns = { #JoinColumn(name="LIBRARY_IMAGE_ID", nullable = false)})
private Set<LibraryImage> itemImages = new HashSet<>();
#OneToMany(fetch = FetchType.LAZY, mappedBy = "rootLibraryItemModel")
private Set<LibraryModel> libraries = new HashSet<>();
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "LIBRARY_ITEM_ATTRIBUTE",
joinColumns = { #JoinColumn(name = "LIBRARY_ITEM_ID", nullable =false)},
inverseJoinColumns = { #JoinColumn(name="LIBRARY_ITEM_ATTRIBUTE_ID", nullable = false)})
private Set<LibraryItemAttribute> libraryItemAttributes = new HashSet<>();
}
LibraryImage:
#Entity
#Table(name = "library_image", uniqueConstraints = {
})
public class LibraryImage {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "LIBRARY_IMAGE_ID", unique = true, nullable = false)
private Integer libraryImageId;
#Column(name = "IMAGE_LOCATION")
private String imageLocation;
#Column(name = "IMAGE_TITLE")
private String imageTitle;
#Enumerated(EnumType.STRING)
private LibraryImageType imageType;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="LIBRARY_ITEM_ID", nullable = false)
private LibraryItemModel libraryItemModel;
}
Library Attribute:
#Entity
#Table(name = "library_item_attribute", uniqueConstraints = {
})
public class LibraryItemAttribute {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "LIBRARY_ITEM_ATTRIBUTE_ID", unique = true, nullable = false)
private Integer libraryItemAttributeId;
#Column(name = "ATTRIBUTE_NAME")
private String attributeName;
#Column(name = "ATTRIBUTE_VALUE")
private String attributeValue;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="LIBRARY_ITEM_ID", nullable = false)
private LibraryItemModel libraryItemModel;
}
This is really frustrating as the LibraryImage class is mapped without problems and doesn't throw any errors, but the LibraryAttribute class which is annotated in an identical way to the LibraryImage class is throwing this error.
Can someone please have a look and let me know what my problem is?
Found the problem.
In the LibraryItemModel class I defined the Join table with the LibraryItemAttribute to be called "LIBRARY_ITEM_ATTRIBUTE", which is the name of the table of the Library item attributes.
The join table is a different table and should have a different table name.
For the Library Image table above, the image table is called library_image, while the join table is called LIBRARY_ITEM_IMAGE

Java JPA error: Missing mapping for field

I'm having problem with Java JPA with persisting entity relationship object. My table: Rezervacija consists of (IDrez, idKorisnik, idKompanija, idKomponenta). I'm persisting new object (and i got all 3 params after IDRez from database, and it's okay. p.s. idRez is being autoincrement by database.
Here's my code:
public class Komponenta implements Serializable {
#Id
private Integer idKom;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "idKomponenta")
private Collection<Rezervacija> rezervacijaCollection = new ArrayList<>();
}
public class Korisnik implements Serializable {
#Id
private Integer idKor;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "idKorisnik")
private Collection<Rezervacija> rezervacijaKorisnikCollection = new ArrayList<>();
#OneToMany(cascade = CascadeType.ALL, mappedBy = "idKompanija")
private Collection<Rezervacija> rezervacijaKompanijaCollection = new ArrayList<>();
}
public class Rezervacija implements Serializable {
#Id
private Integer idRez;
#JoinColumn(name = "idKom", referencedColumnName = "idKomponenta")
#ManyToOne(cascade = CascadeType.ALL)
private Komponenta idKomponenta;
#JoinColumn(name = "idKor", referencedColumnName = "idKorisnik")
#ManyToOne(cascade = CascadeType.ALL)
private Korisnik idKorisnik;
#JoinColumn(name = "idKompanija", referencedColumnName = "idKompanija")
#ManyToOne(cascade = CascadeType.ALL)
private Korisnik idKompanija;
}
// executable code part em - entitymanager
entityManager.getTransaction().begin();
Korisnik korisnik = entityManager.find(Korisnik.class, 1);
Korisnik kompanija = entityManager.find(Korisnik.class, 2);
Komponenta komponenta = entityManager.find(Komponenta.class, 1);
Rezervacija rez = new Rezervacija(korisnik, kompanija, komponenta);
entityManager.persist(rez);
entityManager.getTransaction().commit();
When I run this code I've got console out error:
Exception Description: Missing mapping for field [komponenta.idKomponenta].

Can't write my SQL query in Spring Data JPA custom repository

There is part of SQL i want to realize in my Custom JPA repository
SELECT * FROM users u
JOIN skills_user sku on sku.user_id = u.id
JOIN specs_user spu on spu.user_id = u.id
GROUP BY u.id
HAVING ANY(sku.dictionary_id in (15,20) or spu.dictionary_id in (15,20))
ORDER BY u.id
I tried this:
//Other predicates
if (filterQuery.getSkills() != null && !filterQuery.getSkills().isEmpty()) {
String[] tmp = filterQuery.getSkills().replaceAll(" ", "").split(",");
List<Integer> ids = new ArrayList<>();
for (String s : tmp) {
ids.add(Integer.parseInt(s));
}
List<Predicate> tmpPredicates = new ArrayList<>();
Join<User, Dictionary> skillJoin = root.join("skillList");
Join<User, Dictionary> specsJoin = root.join("specsList");
for (Integer id : ids) {
tmpPredicates.add(builder.or(builder.equal(skillJoin.get("id"), id), builder.equal(specsJoin.get("id"), id)));
}
predicates.add(builder.and(tmpPredicates.toArray(new Predicate[tmpPredicates.size()])));
}
//Other predicates
But it isn't work correctly.
How can i realise this correctly in JPA custom repository?
there is code of User and Dictionary classes:
#Entity
#SequenceGenerator(name = "user_gen", sequenceName = "users_seq")
#Table(name = "users")
public class User {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_gen")
private Long id;
#Column(name = "login")
private String login;
#Column(name = "password")
private String password;
#Column(name = "name")
private String name;
#Column(name = "surname")
private String surname;
#Column(name = "middlename")
private String middlename;
#Column(name = "academic_group")
private String academicGroup;
#Column(name = "entrance_year")
private int entranceYear;
#Column(name = "avatar_URL")
private String avatarURL;
#Column(name = "salt")
private String salt;
#Enumerated(EnumType.ORDINAL)
#Column(name = "user_group")
private UserGroup group;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "SocialRole_User", joinColumns = {
#JoinColumn(name = "user_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "socialRole_id",
nullable = false, updatable = false) })
private List<SocialRole> socialRoleList;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "specs_user", joinColumns = {
#JoinColumn(name = "user_id", nullable = false, updatable = true)},
inverseJoinColumns = {#JoinColumn(name = "dictionary_id",
nullable = false, updatable = true)})
private List<Dictionary> specsList;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "skills_user", joinColumns = {
#JoinColumn(name = "user_id", nullable = false, updatable = true)},
inverseJoinColumns = {#JoinColumn(name = "dictionary_id",
nullable = false, updatable = true)})
private List<Dictionary> skillList;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Contacts> contactsList;
//Getters and setters
}
Dictionary:
#Entity
#SequenceGenerator(name = "dictionary_gen", sequenceName = "dictionary_seq")
#Table(name = "dictionary")
public class Dictionary {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "dictionary_gen")
private Long id;
#Column(name = "dic_name")
private String name;
#Enumerated(EnumType.STRING)
#Column(name = "dic_type")
private DictionaryType type;
// Getters and Setters
}
Have you tried writing the query using JPQL?
SELECT a FROM User a
INNER JOIN a.specsList b
INNER JOIN a.skillList c
GROUP BY a.id
HAVING ANY(b.id in (15,20) OR c.id in (15,20))
ORDER BY a.id;
This JPQL should work the same as your plain SQL.

Categories

Resources