How to map an abstract class without an id in Hibernate - java

The background of this project is that there are two types of users, suscriber users and amdin users. There are also two types of sucriber users, students and professors. Admin users can register new classrooms, and suscriber users can suscribe to classrooms to see different information such as temperature etc.
The problem is that I have to map a ManyToMany bidirectional relationship between clasrooms and suscriber users and I'm getting the following error in the Classroom class:
'Many To Many' attribute value type should not be 'SuscriberUser'
and this exception:
org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class
This is my code:
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class User implements IUser {
private String name;
// some other fields
// constructors
// getters and setters
}
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class SuscriberUser extends User implements ISuscriberUser {
#ManyToMany(mappedBy = "suscribers")
private ArrayList<Classroom> classroomSubscriptions;
// constructors
// getters and setters
}
For example one concrete class of SuscriberUser:
#Entity
#Table(name = "student")
public class Student extends SuscriberUser {
#Id
private int studentId;
// constructors
// getters and setters
}
#Entity
#Table(name = "classroom")
public class Classroom implements IClassroom {
#Id
private int internalId;
// other fields
#ManyToMany()
#JoinTable(name = "suscribers")
private ArrayList <SuscriberUser> suscribers;
// constructors
// getters and setters
}
I have also tried using #MappedSuperclass in both classes User and SuscriberUser, but it doesn't work. I guess it's because both abstract classes don't have an id.
How can I solve this?

The User class is just a collector of fields, therefore it can become a #MappedSuperClass.
#MappedSuperClass
public abstract class User implements IUser {
private String name;
// some other fields
// constructors
// getters and setters
}
If you use #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS), you have a table per class, so you need to:
remove abstract
add #Entity annotation
add #Table to define a name
add #Id to the id column.
#Entity
#Table(name = "subscriber_user")
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class SuscriberUser extends User implements ISuscriberUser {
#Id
private int id;
#ManyToMany(mappedBy = "suscribers")
private List<Classroom> classroomSubscriptions;
// constructors
// getters and setters
}
More info here.
The student class does not need an id column because is in the parent class.
#Entity
#Table(name = "student")
public class Student extends SuscriberUser {
// constructors
// getters and setters
}
Pay attention that the join table is another one. This table contains the relations between students and classrooms and it can be named subscription.
#Entity
#Table(name = "classroom")
public class Classroom implements IClassroom {
#Id
private int internalId;
// other fields
#ManyToMany
#JoinTable(
name = "subscription",
joinColumns = #JoinColumn(name = "internalId"), // id class room
inverseJoinColumns = #JoinColumn(name = "id")) // id user
private List<SuscriberUser> suscribers;
// constructors
// getters and setters
}

Related

JPA - An entity's property type depends on a column value

I'm working with a 3rd party database with the following table:
Node
----
id (int not null)
fKey (varchar(max) null)
refTable (varchar(max) null)
fkey is the value of the primary key of refTable. refTable contains one the following values Car, Truck, or Boat. My node entity currently looks like:
#Entity
#Data
public class Node {
#Id
private Long id;
#Column(name = "fKey")
private String foreignKey;
#Column(name = "refTable")
private String targetTable;
}
But I'd prefer the node entity refer to a Car entity when targetTable is Car. Something like this:
#Entity
#Data
public class Node {
#Id
private Long id;
#ManyToOne
#JoinColumn(name = "carId")
private Car car;
// ok with having truck and boat
// here too and being null when refTable refers to Car
}
How can I set up this mapping?
A better aproach would be to use inheritance. You should turn your Node class the MainClass and inherit from it to Car, Truck and Boat
#Entity
#Data
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = “refTable”)
public class Node {
#Id
private Long id;
}
Your subclassed implementation should look like
#Entity
#Data
#DiscriminatorValue(“Car”)
public class CarNode extends Node {
#ManyToOne
#JoinColumn(name = "fKey")
private Car car;
}
The same for Truck and Boat. Your query for Nodes would return you CarNodes, TruckNodes or BoatNodes

JPA: OneToMany relationship keeps the Collection empty

It seems I am having a difficult time understanding JPA and how the OneToMany relationship actually works.
For example, imagine I have an object Class
#Entity
public class Class {
#Id
private String className;
#OneToMany(cascade = Cascade.ALL, orphanRemoval = true)
private Set<Student> students;
// Constructors, Getters, Setter
}
I also have an object Student where it holds Class.
#Entity
public class Student {
#Id
private String studentName;
#ManyToOne
private Class class;
// Constructors, Getters, Setter
}
Obviously, a student can have multiple classes but forget about that.
Why is that when I build a class and then build a couple students using that class, findAll() on the ClassRepository returns me an empty set of students.
Class class = new Class("CS", new HashSet<>());
classRepository.save(class); // repository has no special functions
Student student1 = new Student("1", class);
Student student2 = new Student("2", class);
studentRepository.save(student1);
studentRepository.save(student2);
classRepository.findAll() // Returns me a List<Class> with only one class object that has an empty set.
I was thinking the above code should automatically see that the two students are from that one class and so when I call buildingRepository.findAll(), it will return a Class object with the students set populated properly.
Is my understanding wrong then? Or is my code wrong? And how can I change it up to fix it?
You can choose:
1. Unidirectional #OneToMany:
#Entity
public class Class {
#Id
private String className;
#OneToMany(cascade = Cascade.ALL, orphanRemoval = true)
private List<Student> students=new ArrayList<>();
// Constructors, Getters, Setter
}
#Entity
public class Student {
#Id
private String studentName;
// Constructors, Getters, Setter
}
Now, if we persist one Class:
Class class1=new Class("name1");
class1.getStudents().add(new Student("student1Name"));
// then you can make a save of class1 in db
classRepository.save(class);
2. Unidirectional #OneToMany with #JoinColumn:
To fix the aforementioned extra join table issue, we just need to add the #JoinColumn in the mix:
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "class_id")
private List<Student> students = new ArrayList<>();
3. Bidirectional #OneToMany:
The best way to map a #OneToMany association is to rely on the #ManyToOne side to propagate all entity state changes:
#Entity
public class Class {
#Id
private String className;
#OneToMany(
mappedBy = "class",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Student> students=new ArrayList<>();
// Constructors, Getters, Setter
public void addStudent(Student student) {
students.add(student);
student.setClass(this);
}
public void removeStudent(Student student) {
students.remove(student);
student.setClass(null);
}
}
#Entity
public class Student {
#Id
private String studentName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "class_id")
private Class class;
}
And to persist:
Class c1=new Class("className1");
c1.addStudent(new Student("StudentNAme1"));
c1.addStudent(new Student("StudentNAme2"));
c1.addStudent(new Student("StudentNAme3"));
classRepository.save(c1);

Hibernate - adding two class in one table

How i can add based and descendant class in one table with Hibernate?
Based class
public class Id{
protected int id
}
Descendant class
public classs User exetends Id{
public String username;
public String password;
}
In one table USERS with properties: id, username, password.
From what i understood.
You want to have a Base entity. And that Base entity should be extended by other Descendant entities. In other words the Descendant should be having all the properties from the Base entity. One use case for this design is to have common properties(id, createdDate, updatedDate) for all Descendant class in a Base entity class.
One way to go with this is:
BaseEntity(Base Class)
#MappedSuperclass
public class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
//..setter and getters
}
User(Descendant Class)
#Entity
#Table(name = "user")
public class User extends BaseEntity {
#Column(name = "name")
private String name; //User specific properties
//..setters and getters here
}
Firstly you need to mark them with #Entity to show that they are tables, then in the top level set the table name with #Table. To use inheritance, you need to select a strategy: for this instance you want SINGLE_TABLE. Then in order to select the correct type a #DiscriminatorColumn is required, setting the column name and the type of discriminator - in this case I have chosen INTEGER. You'll need to also add to the type annotation updatable = false and insertable = false so they cannot be modified.
#Entity
#Table(name = "TABLE_NAME")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "TYPE",discriminatorType = DiscriminatorType.INTEGER)
public class Id {
#Column(name="TYPE", insertable=false, updatable=false)
private Integer type;
}
On your sub classes you need to mark them with #Entity and a #DiscriminatorValue (in this case 1).
#Entity
#DiscriminatorValue(value = "1")
public classs User exetends Id{
public String username;
public String password;
}

JPA Annotations with inheritance

I am working on JPA project and I need your help.
I have two classes, “Person” and “Leader” which inherits from Person.
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(unique = true)
private String personId;
}
And
#Entity
public class Leader extends Person implements Serializable {
private List < Person > listTeam;
public void addPersonInTeam(Person e) {
listTeam.add(e);
}
}
My question Is, do I need to have JPA annotations #OneToMany or something else before private List listTeam in class Leader?
Thank you very much
You need to specify a mapping between the two classes because for Hibernate the association is not relevant here, you have to use annotations in both sides and I guess you will need a OneToMany mapping here :
Here's the mapping that you are seraching for:
In Person class:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(unique = true)
private String personId;
#ManyToOne
#JoinColumn(name="leader_id")
private Leader leader;
//getter and setter
}
In Leader class:
#Entity
public class Leader extends Person implements Serializable {
#OneToMany(mappedBy = "leader")
private List <Person> listTeam;
//getter and setter
public void addPersonInTeam(Person e) {
listTeam.add(e);
}
}
For further information you can see these links:
Hibernate – One-to-Many example (Annotation).
Hibernate One To Many Annotation tutorial.
Note:
I don't see the use of the field personId in the Person class, there's no need to use two differents ids.
EDIT:
To answer your questions:
The #JoinColumn(name="leader_id") is not mandatory, but it's used to specify the foreign key name.
If the relation is ManyToMany the mappedBy property is used to specify the owner of the relationship, you can see this answer for more details.

QueryDSL query exception

I have a problem with a QueryDSL query. Classes:
#Entity
#Table(name="project")
#Cacheable(true)
#Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Project extends DomainObject implements Comparable<Project>, IconizedComponent, Commentable {
#ManyToMany(targetEntity=Student.class)
#JoinTable(name="project_student")
#Sort(type=SortType.NATURAL) //Required by hibernate
#QueryInit({"user"})
private SortedSet<Student> projectParticipants = new TreeSet<Student>();
private Project(){}
//attributes, get+set methods etc
}
#Entity
#Cacheable(true)
#Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) //Hibernate specific
public class Student extends Role {
public Student(){}
//attributes, get+set methods etc
}
#Entity
#DiscriminatorColumn(name = "rolename", discriminatorType = DiscriminatorType.STRING, length = 8)
#Table(name="role", uniqueConstraints={#UniqueConstraint(columnNames={"user_id","rolename"}, name = "role_is_unique")})
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class Role extends LazyDeletableDomainObject implements Comparable<Role> {
#ManyToOne(optional=false)
protected User user;
public Role(){}
//attributes, get+set methods etc
}
#Entity
#Table(name="user")
#Cacheable(true)
#Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) //Hibernate specific
public class User extends LazyDeletableDomainObject implements Comparable<User>, IconizedComponent {
private String firstName;
private String lastName;
public User(){}
//attributes, get+set methods etc
}
Query:
private BooleanExpression authorsNameContains(String searchTerm){
QUser user = new QUser("user");
user.firstName.containsIgnoreCase(searchTerm).or(user.lastName.contains(searchTerm));
QStudent student = new QStudent("student");
student.user.eq(user);
return QProject.project.projectParticipants.contains(student);
//java.lang.IllegalArgumentException: Undeclared path 'student'. Add this path as a source to the query to be able to reference it.
}
I have also tried annotating the projectParticipants set in Project with
#QueryInit("*.*")
But that gives the same exception. Any hints?
#Timo Westkämper
#siebZ0r
Thanks for your attention. Sorry for the delayed reply and incorrectly phrased question. Actually what I wanted to do was to write a working BooleanExpression.
In combination with the annotations already made, this was what I was after:
private BooleanExpression authorsFirstNameContains(String searchTerm){
return QProject.project.projectParticipants.any().user.firstName.containsIgnoreCase(searchTerm);
}
I got this right with the help of a colleague.

Categories

Resources