Hibernate #embededId containing a foreign key of an entity with inheritance - java

Used this as my base point: https://vladmihalcea.com/the-best-way-to-map-a-many-to-many-association-with-extra-columns-when-using-jpa-and-hibernate/
I have an entity named Attendance that has an and emebedid AttendacneId that has two columns lectureId (string) and studenId(UUID). The attendance entity also has other properties as well containing a ManyToOne relationship with student and lecture enity using the #MapsId on them. The student extends user entity where the id resides. now when I try to save anything I get this error.
java.util.NoSuchElementException
at java.util.ArrayList$Itr.next(ArrayList.java:860)
at org.hibernate.cfg.annotations.TableBinder.linkJoinColumnWithValueOverridingNameIfImplicit(TableBinder.java:724)
at org.hibernate.cfg.PkDrivenByDefaultMapsIdSecondPass.doSecondPass(PkDrivenByDefaultMapsIdSecondPass.java:37)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1684)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1641)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:286)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:83)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:473)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:84)
at utility.Utils.<clinit>(Utils.java:60)
at utility.Testing.main(Testing.java:21)
My AttendanceId class
#Embeddable
public class AttendanceId implements Serializable {
#Column(name = "lecture_fid")
#Getter
#Setter
private String lectureId;
#Column(name = "student_fid", columnDefinition = "uuid")
#Getter
#Setter
private UUID studentId;
public AttendanceId() {}
}
My Attendance class
#Entity
#Table(name = "attendance")
public class Attendance implements Serializable {
#EmbeddedId
#Getter
#Setter(AccessLevel.PRIVATE)
private AttendanceId id;
// other fields
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("lectureId")
private Lecture lecture;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("studentId")
private Student student;
}
My Student Class
#Entity
#PrimaryKeyJoinColumn(name = "id")
public class Student extends User implements Serializable,
Comparable<Student> {
// other fields
#OneToMany(mappedBy = "student", fetch = FetchType.LAZY)
#Getter
private List<Attendance> attendances = new ArrayList<>();
}
My User class
#Entity
#Table(name = "users", uniqueConstraints = {
#UniqueConstraint(columnNames = {"email", "username"})})
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class User implements Serializable {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid")
#Column(columnDefinition = "UUID")
#Getter(AccessLevel.PUBLIC)
#Setter(AccessLevel.PUBLIC)
private UUID id;
// other fields.
}
My Lecture Class
#NamedNativeQueries(
#NamedNativeQuery(name = "getLectureId", query = "select
get_lecture_id()")
)
#Entity
#Table(name = "lecture")
public class Lecture implements Serializable {
#Id
#Column(name = "id")
#GenericGenerator(name = "lectureIdGenerator",
strategy = "entities.LectureIdGenerator")
#GeneratedValue(generator = "lectureIdGenerator")
#Getter
#Setter
private String id;
#OneToMany(mappedBy = "lecture", fetch = FetchType.LAZY)
#Getter
private List<Attendance> attendances = new ArrayList<>();
// other fields
}
I think the inheritance is causing an issue. There are other classes/entities as well that extends the user entity. If anyone could help me find the problem that will be great. Thank you.

This issue is caused by the HHH-7135 Hibernate issue.

Related

Accessing #JoinTable in many-to-many relationship in Springboot JPA

I've created an API that has actor, movie and category entities. Actor and movie are connected by many-to-many relationship that maps to a join table called movie_actor and category is connected with movie by one-to-many relationship.
I'm trying to write a native query that returns an integer that would represent the amount of movies from a specific category where specific actor has played so for example query would return 2 if actor played in 2 different sci-fi movies. I have no problem doing that from the database level where I can see the join table movie_actor but that table remains unaccessible in my api because it's not a separate entity. How can I create it that it automatically maps actor and movie ids as the movie_actor table ?
Here is an example code that works for me in the H2 Database:
SELECT COUNT(*) FROM MOVIE M JOIN MOVIE_ACTOR MA on M.MOVIE_ID = MA.MOVIE_ID WHERE ACTOR_ID = 1 AND CATEGORY_ID = 1
Here are my entities:
Actor:
#Entity
#Data
#Table(name = "actor")
#AllArgsConstructor
#NoArgsConstructor
public class Actor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "actor_id")
private long actorId;
#Column(name = "name")
private String name;
#Column(name = "surname")
private String surname;
#Nullable
#ManyToMany(mappedBy = "actors", fetch = FetchType.EAGER)
#JsonBackReference
private List<Movie> movies = new ArrayList<>();
public Actor(String name, String surname){
this.name = name;
this.surname = surname;
}
}
Movie:
#Entity
#Data
#Table(name = "movie")
#AllArgsConstructor
#NoArgsConstructor
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "movie_id")
private long movieId;
#Column(name = "title")
private String title;
#ManyToMany
#JoinTable(
name = "movie_actor",
joinColumns = #JoinColumn(name = "movie_id"),
inverseJoinColumns = {#JoinColumn(name = "actor_id")}
)
#JsonManagedReference
private List<Actor> actors = new ArrayList<>();
#ManyToOne
#JoinColumn(name = "CATEGORY_ID")
#JsonManagedReference
private Category category;
}
So you have to make it accessible in your API. One option would be to map the intersection table movie_actor to the entity MovieActor and split ManyToMany relationship between Actor and Movie to OneToMany relationship with MovieActor, like that:
#Entity
#Data
#Table(name = "movie_actor")
#AllArgsConstructor
#NoArgsConstructor
public class MovieActor {
#EmbeddedId
private MovieActorId productOrderId = new MovieActorId();
#ManyToOne(cascade = CascadeType.ALL, optional = false)
#MapsId("movieId")
#JoinColumn(name = "product_id", nullable = false)
private Movie movie;
#ManyToOne(cascade = CascadeType.ALL, optional = false)
#MapsId("actorId")
#JoinColumn(name = "order_id", nullable = false)
private Actor actor;
public void addMovieActor(Movie aMovie, Actor aActor) {
movie = aMovie;
actor = aActor;
aMovie.getMovieActors().add(this);
aActor.getMovieActors().add(this);
}
}
#Embeddable
#Getter
#Setter
public class MovieActorId implements Serializable {
#Column(name = "movie_id")
private Long movieId;
#Column(name = "actor_id")
private Long actorId;
}
#Entity
#Data
#Table(name = "actor")
#AllArgsConstructor
#NoArgsConstructor
public class Actor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "actor_id")
private long actorId;
#OneToMany(mappedBy = "actor")
private List<MovieActor> movieActors = new ArrayList<>();
}
#Entity
#Data
#Table(name = "movie")
#AllArgsConstructor
#NoArgsConstructor
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "movie_id")
private long movieId;
#OneToMany(mappedBy = "movie")
private List<MovieActor> movieActors = new ArrayList<>();
}
Now you can access the intersection table MovieActor inside the query. You can even add more columns to this table if you want.

How to get parent entity with all child entities and child entities of children in Spring/JPA/Hibernate with Lombok

I have these entities where Shop entity is parent:
#Data
#NoArgsConstructor
#Entity
#DynamicUpdate
#Table(name = "Shop", schema = "public")
public class ShopDao {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private String name;
private String processedStatus;
#OneToMany(mappedBy = "shopDao", cascade = CascadeType.ALL, orphanRemoval = true)
private List<BookDao> bookDaoList;
}
#Data
#NoArgsConstructor
#Entity
#ToString(exclude = {"shopDao"})
#Table(name = "Book", schema = "public")
public class BookDao {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private String name;
private String author;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "other_id", referencedColumnName = "id")
private OtherDao otherDao;
#ManyToOne
#JoinColumn(name = "shop_id", nullable = false)
private ShopDao shopDao;
}
#Data
#NoArgsConstructor
#Entity
#ToString(exclude = {"bookDao"})
#Table(name = "Other", schema = "public")
public class OtherDao {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private String metadata;
#OneToOne(mappedBy = "otherDao", fetch = FetchType.EAGER)
private BookDao bookDao;
}
And these are repos:
#Repository
public interface ShopRepo extends JpaRepository<ShopDao, Long> {
#EntityGraph(attributePaths = {"bookDaoList"})
List<ShopDao> findAllByProcessedStatus(String processedStatus);
}
#Repository
public interface BookRepo extends JpaRepository<BookDao, Long> {
}
#Repository
public interface OtherRepo extends JpaRepository<OtherDao, Long> {
}
When i'm using findAllByProcessedStatus() function, i get BookList inside Shop object correctly, but each Book can't reach their Other objects and i get LazyInitializationException:
screenshot
How do i fix that problem?
Actually, with spring data's #EntityGraph all you need is :
#Repository
public interface ShopRepo extends JpaRepository<ShopDao, Long> {
#EntityGraph(attributePaths = {"bookDaoList.otherDao"})
List<ShopDao> findAllByProcessedStatus(String processedStatus);
}
This is the most convenient way.
For more complex relations, you could define a #NamedEntityGraph, and provide subgraphs, like so.
What I find intriguing, is that the BookDao is the owner of this relation, so I would expect it to be eagerly loaded, since you haven't specified a the Lazy fetch mode explicitly ...

Hibernate one to one criteria fetching

I am using hibernate one to one mapping between car and person table. But a person might have car and might not have a car. now while fetching the records from the person table using hibernate criteria , I want to fetch only those persons who have a car, i.e only those entries in person table for which a corresponding entry in car table exists. How this can be done using hibernate criteria/alias?
Below is the piece of code. kindly help with the criteria or alias that has to written to fetch result:
#Getter
#Setter
#Entity
#Table(name = "Person")
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "Person_ID")
private Long personId;
#OneToOne(mappedBy = "personAsset", cascade = CascadeType.ALL)
private Car car;
}
#Getter
#Setter
#Entity
#Table(name = "Car")
public class Car implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "Car_ID")
private Long carId;
#OneToOne
#JoinColumn(name = "Person_ID")
private Person personAsset;
}
what you are looking for is the cascadeType orphanRemoval=true on the #OneToOne annotation.
here is your class how would look like :
#Getter
#Setter
#Entity
#Table(name = "Car")
public class Car implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "Car_ID")
private Long carId;
#OneToOne(fetch=FetchType.EAGER , cascade=CascadeType.ALL, orphanRemoval=true)
#JoinColumn(name = "Person_ID")
private Person personAsset;
}
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Person> query = cb.createQuery(Person.class);
Root<Person> person = query.from(Person.class);
Predicate predicate = cb.isNotNull(person.join("car"));
predicates.add(predicate );

Hibernate - One to one relation single table inheritance

I have 3 entities BaseFoo, FooAbc, FooAbcDetail. FooAbc extends base entity BaseFoo. I'm trying to do one to one relation between FooAbc and FooAbcDetail.
#Data
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "foo")
#Audited
#AuditOverride(forClass = Auditable.class)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type_id", discriminatorType = DiscriminatorType.INTEGER)
public abstract class BaseFoo extends Auditable implements Serializable {
#Id
#Column(name = "id")
private Long id;
//other fields
}
#Data
#EqualsAndHashCode(callSuper = true)
#Entity
#Audited
#AuditOverride(forClass = BaseFoo.class)
#DiscriminatorValue("2")
public class FooAbc extends BaseFoo implements Serializable {
#EqualsAndHashCode.Exclude
#ToString.Exclude
#NotAudited
#OneToOne(mappedBy = "fooAbc",
fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
orphanRemoval = true,
optional = false)
private FooAbcDetail fooAbcDetail;
//other fields
}
#Data
#Entity
#Table(name = "foo_abc_detail")
public class FooAbcDetail implements Serializable {
#Id
#Column(name = "foo_id"/* foo_abc_id (I tried both) */)
private Long id;
#ToString.Exclude
#EqualsAndHashCode.Exclude
#MapsId //also tried #MapsId("id")
#OneToOne(fetch = FetchType.LAZY)
private FooAbc fooAbc; //also tried BaseFoo
//other fields
}
While project starting up Hibernate throws:
org.hibernate.MappingException: Unable to find column with logical
name: id in org.hibernate.mapping.Table(public.foo_abc_detail) and its
related supertables and secondary tables
What is the problem here?
Environment
Hibernate 5.3.10.Final
Spring Boot 2.1.7.RELEASE
This error is telling you that there is no column on the foo_abc_detail table called foo_id.The column on foo_abc_detail is just called id.
#Data
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "foo")
#Audited
#AuditOverride(forClass = Auditable.class)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type_id", discriminatorType = DiscriminatorType.INTEGER)
public abstract class BaseFoo extends Auditable implements Serializable {
#Id
#Column(name = "id")
private Long id;
//other fields
}
#Data
#EqualsAndHashCode(callSuper = true)
#Entity
#Audited
#AuditOverride(forClass = BaseFoo.class)
#DiscriminatorValue("2")
public class FooAbc extends BaseFoo implements Serializable {
#EqualsAndHashCode.Exclude
#ToString.Exclude
#NotAudited
#OneToOne(mappedBy = "fooAbc",
fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
orphanRemoval = true,
optional = false)
private FooAbcDetail fooAbcDetail;
//other fields
}
#Data
#Entity
#Table(name = "foo_abc_detail")
public class FooAbcDetail implements Serializable {
#Id
#Column(name = "id")
private Long id;
#ToString.Exclude
#EqualsAndHashCode.Exclude
#MapsId //also tried #MapsId("id")
#OneToOne(fetch = FetchType.LAZY)
private FooAbc fooAbc; //also tried BaseFoo
//other fields
}
Okey I solved. I don't know if this is the best way to do it but it works! I replaced the #MapsId annotation via #JoinColumn. Final result of the FooAbcDetail class like as below.
#Data
#Entity
#Table(name = "foo_abc_detail")
public class FooAbcDetail implements Serializable {
#Id
#Column(name = "id")
private Long id;
#ToString.Exclude
#EqualsAndHashCode.Exclude
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id", referencedColumnName = "id", nullable = false)
private FooAbc fooAbc;
}

MappedBy reference an unknown target entity

I design a system of tables in the database for the film service. So far I have designed them in this way.
#Entity
#Table(name = "movies")
#Data
public class MovieEntity {
#Id
#Column(unique = true, updatable = false)
#GeneratedValue
private Long id;
#OneToMany(mappedBy = "movie", cascade = CascadeType.ALL)
private Set<MovieDescription> description;
}
#Entity
#Table(name = "movies_info")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type")
public abstract class MovieInfo {
#Id
#Column(unique = true, updatable = false)
#GeneratedValue
private Long id;
#ManyToOne
public MovieEntity movie;
}
#Entity
#DiscriminatorValue(value = EditType.Values.DESCRIPTION)
public class MovieDescription extends MovieInfo {
private String description;
private String language;
}
When compiling, it sends me a mistake
Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: com.core.jpa.entity.MovieDescription.movie in com.core.jpa.entity.MovieEntity.description
Something related to MovieEnity mapping, but I don't know what it is all about.
use targetEntity attribute to target the super class field that you want to map with.
#Entity
#Table(name = "movies")
#Data
public class MovieEntity {
#Id
#Column(unique = true, updatable = false)
#GeneratedValue
private Long id;
#OneToMany(mappedBy = "movie", cascade = CascadeType.ALL, targetEntity= MovieInfo.class)
private Set<MovieDescription> description;
}
More detail: one to many mapping to a property of superclass

Categories