I am making a POS like system. I wonder how to map subclass using JPA (this is for my DAO). Product class has product details and OrderProduct class has information about the Product and details about the order.
#Entity
#Table(name="products")
public class Product implements Serializable{
#Id
#Column(name="id")
#GeneratedValue(strategy = GenerationType.AUTO
public int getId(){ return id;}
/**
Other get/set methods
*/
}
#Entity
#Table(name="order_products")
public class OrderProduct extends Product{
#Id
#Column(name="id")
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId(){ return id;}
/**
Other get/set methods
*/
}
I got complain about duplicate #Id. But OrderProduct class really need another id than the product one. How should I map this?
DB is something like this
Table products
id int
name varchar(32)
Table order_product
id int
quantity int
productid int fk referencing product table
Would #IdClass or #AttributeOverride help?
I don't think that what you'd like to achieve is possible neither with JPA nor Hibernate. When using a joined subclass strategy, the PK of the subclass is the the PK of the base class and is used to perform the join between the tables. From the JPA specification:
2.1.10.3 Joined Subclass Strategy
In the joined subclass strategy, the
root of the class hierarchy is
represented by a single table. Each
subclass is represented by a separate
table that contains those fields that
are specific to the subclass (not
inherited from its superclass), as
well as the column(s) that represent
its primary key. The primary key
column(s) of the subclass table serves
as a foreign key to the primary key of
the superclass table.
You could use a #PrimaryKeyJoinColumn to change the column name in the child class:
#Entity
#PrimaryKeyJoinColumn(name="PRODUCT_ID", referencedColumnName = "ID")
public class OrderProductTop extends ProductTop {
...
}
But PRODUCT_ID would still be the PK.
And to be honest, I don't understand your physical model. To me, what you currently have represented is a one-to-many relationship, which is not correct (it should be a one-to-one).
Related
According to the Spring JPA documentation, in the Many-To-Many relationship (student - course) we must create a new table (student_course)
class student ---> class student_course <--- class course
According to the documentation, if we want to add a new property to the table (student_course) we must create a new class that will contain the compound keys of the student class and the course class
#Embeddable
class CourseStudentKey implements Serializable {
#Column(name="student_id")
Long studentId;
#Column(name = "course_id")
Long courseId;
}
_ Then to the Student_Course class we assign the id of type CourseStudentKey that contains the compound keys:
#Entity
class StudentCourse {
#EmbeddedId
CourseRatingKey id;
#ManyToOne
#MapsId("studentId")
#JoinColumn(name = "student_id")
Student student;
#ManyToOne
#MapsId("courseId")
#JoinColumn(name = "course_id")
Course course;
}
My question is: What is the difference in creating only the StudentCourse class and doing the #ManyToOne mapping to the Student class and the Course class??... in this way we can also add attributes to the StudentCourse class
_Clase Student
#Entity
class Student {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private idStudent;
#JsonIgnore
#OneToMany(mappedBy = "student")
List<StudentCourse> studentCourses = new ArrayList<>();
_Clase Course
#Entity
class Course{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private idCourse;
#JsonIgnore
#OneToMany(mappedBy = "course")
List<StudentCourse> studentCourses = new ArrayList<>();
}
_Clase StudentCourse
#Entity
class StudentCourse {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private idStudentCourse;
#ManyToOne
#JoinColumn(name = "student_id")
Student student;
#ManyToOne
#JoinColumn(name = "course_id")
Course course;
}
The only difference in the examples posted by you, is, in case of Embeddable, the student_id course_id would be a composite key, so there would only be one row allowed per student_id course_id combination. Whereas, in the second example, you have used generated primary key, ensuring multiple rows for each student_id course_id combination. This would be particularly useful if the student fails the course for the first time and attempts it again. You can then add parameters like attemped_on, is_completed, etc. to the student_course entity
Your examples show differences in the key, and as Chetan's answer states, this affects the key used in the table. The choices here isn't necessarily in using a separate class/embbeded class, but in using a single generated Identifier vs using a composite primary key for the entity.
In the embedded example you've posted, you have a composite primary key based on foreign key mappings. There are many other ways to map this same setup though, but the common parts will be:
composite PKs need an ID class. It doesn't have to be embedded in your class (see JPA derived IDs) but does need to exist. This is part of the JPA spec and allows em.find operations to deal with a single object.
ID values are immutable. They cannot change without remove/persist operations as per the JPA specification. Many providers don't like you even attempting to modify them in an Entity instance. In your embeddable example, you cannot change the references, while in the generated id example, you can.
It also affects what JPA requires you to use in foreign keys. If you use a composite ID, any references to that entity (*ToOne) that require foreign keys to that table are required to use its defined IDs - all columns that make up that ID. Some providers don't enforce this, but it will affect entity caching; since entities are cached on their IDs, using something else as the target of FKs might mean database hits for entities already in the cache.
I have an existing table without discriminator column and I need to map it with single table hierarchy, how to map this?
here is my table
vehicle
========
vehicle_id
manufacturer
load_capacity
passenger_capacity
I have 3 classes
Vehicle.java
vehicleId;
manufacturer;
TransportationVehicle.java
loadCapacity;
PassengerVehicle.java
passengerCapacity;
If you have no discriminator, then you need another condition to distinguish TransportationVehicle and PassengerVehicle. I guess in your case you could use:
TransportationVehicle: loadCapacity IS NOT NULL
PassengerVehicle: passengerCapacity IS NOT NULL
As JPA inheritance always needs a discriminator, you will have to use #MappedSuperclass and two #Entity classes with a corresponding #Where annotation:
#MappedSuperclass
public abstract class Vehicle {
#Id
private Long vehicleId;
private String manufacturer;
}
#Entity #Where(clause = "loadCapacity IS NOT NULL")
public class TransportationVehicle extends Vehicle {
private int loadCapacity;
}
#Entity #Where(clause = "passengerCapacity IS NOT NULL")
public class PassengerVehicle extends Vehicle {
private int passengerCapacity;
}
Major disadvantage of this approach compared with real JPA inheritance: You can't reference the mapped superclass in JPA - neither in queries (SELECT v FROM Vehicle v) nor in another entity (#OneToOne Vehicle vehicle).
One way to do it is use #MappedSuperclass on the Vehicle class and then map TransportationVehicle and PassengerVehicle to the same table.
This way you will inherit the mapped fields from Vehicle without having to map it as an entity.
I have 3 classes:
#MappedSuperclass
public class BaseEntity {
#Id
#GeneratedValue
private Long id;
...
}
#Entity
public class Person extends BaseEntity {
private String name;
...
}
#Entity
#AttributeOverride(name = "id", #Column = (name = "idStudent"))
public class Student extends Person {
private float avgGrades;
...
}
I would like to override ID property so in Student table there would be a idStudent column instead of id. Unfortunately, the code above doesn't work. It looks like #AttributeOverride only works for the class you extending (and no one step further). Is there any way to override attribute name in situation I've descried (override attribute in class which exteds our class being extended) ?
Your problem is very easy to understand, if you know what the default inheritance type is: SINGLE_TABLE.
That means all entities that extending Person are in the same table. And thus Person already defines the ID column. Because you would otherwise violate the contract of the primary key column of your Person table.
I cite the JavaDoc of #AttributeOverride as well:
May be applied to an entity that extends a mapped superclass or to an embedded field or property to override a basic mapping or id mapping defined by the mapped superclass or embeddable class (or embeddable class of one of its attributes).
It always helps to read the JavaDoc first, before asking questions here.
What can you do about it? Make your Person a #MappedSuperclass (or create a BasePerson that is one).
My data model consists of items with a history. I'll call a point in time an "instant"; all tables therefore have an "instant_id" that specifies how that item was configured at that instant. The "instant_id" is rolled into a composite primary key for all tables. Imagine the following example:
Table Computer
============
PK int instant_id <-- Shared id
PK int computer_id <-- Child id
int computer_type_id <-- Parent id
varchar foo
Table ComputerType
==================
PK int instant_id <-- Shared id
PK int computer_type_id <-- Parent id
varchar bar
There is a foreign key in Computer mapping (instant_id, computer_type_id) to the ComputerType primary key.
We use something like
#Embeddable ComputerId {
#Column(name='instant_id', nullable=false) int instant_id,
#Column(name='computer_id', nullable=false) int computer_id
}
Then:
Computer {
#EmbeddedId ComputerId id;
#MapsId('instant_id')
#ManyToOne
#JoinColumns({
#JoinColumn(name='instant_id',...),
#JoinColumn(name='computer_type_id',...)
})
ComputerType computerType;
}
No matter how I combine MapsId with JoinColumns, I can't seem to get this to work. Any ideas?
I don't see a ManyToOne association. You are not showing us how ComputerType is declared, I am assuming it's an Entity. If that's the case, per table definition you provided, both Computer and ComputerType share a composite primary key: instant_id and computer_type_id.
If that is true and they share the same primary key, you might be better off normalizing those two tables into one table.
I think I understand the issue now. You need to consider the computer_type_id as part of the composite key for Computer table as well. The column computer_type_id by itself is not quite meaningful; in the ComputerType table it is a part of the primary key, the other part being instant_id. So if that's the case, you need to include it as part of the primary key for Computer table as well, because you will never have a case where Computer.instant_id = ComputerType.instant_id AND Computer.computer_type_id <> ComputerType.computer_type_id, for a given related association. (If I understand this case correctly)
If you agree with that, then here is the solution:
#Embeddable
public class ComputerId implements Serializable {
int computer_id;
#ManyToOne
#JoinColumns({#JoinColumn(name = "instant_id", insertable=false, updatable=false),
#JoinColumn(name = "computer_type_id", insertable=false, updatable=false) })
ComputerType computerType;
// getters and setters
}
#Entity
public class Computer {
#EmbeddedId
ComputerId computerId;
// getters and setters
}
public class ComputerTypeId implements Serializable {
#Column(name="instant_id", nullable=false) int instant_id;
#Column(name="computer_type_id", nullable=false) int computer_type_id;
// getters and setters
}
#Entity
public class ComputerType {
#EmbeddedId
ComputerTypeId computerTypeId;
String bar;
// getters and setters
}
Finally, you might want to consider Hibernate Envers for Entity versioning.
Hope this helps.
I am using Hibernate and have two tables, STUDENTS and DORM_ROOMS, that are related with a composite key:
STUDENTS table:
CAMPUS(String) Part of Composite Key
STUDENT_ID (int) Part of Composite Key
NAME (String)
...
DORM_ROOMS table:
CAMPUS(String) Part of Composite Key
STUDENT_ID (int) Part of Composite Key
ROOM_NUMBER(int)
...
The relationship is one to one because a student can be associated with exactly one dorm room and and a dorm room is associated with one student (wow - a private room!). However, not all students have a dorm room.
My initial code (stripped down) looks like:
FOR STUDENTS:
#Embeddable
public class StudentsPK implements Serializable {
#Column(name = "CAMPUS")
private String Campus;
#Column(name = "STUDENT_ID")
private String StudentID;
...
}
#Entity
#Table(name = "STUDENTS")
public class Students implements Serializable {
#EmbeddedId
private StudentsPK studentsPK;
...
}
FOR DORM_ROOMS:
#Embeddable
public class DormRoomsPK implements Serializable {
#Column(name = "CAMPUS")
private String Campus;
#Column(name = "STUDENT_ID")
private String StudentID;
...
}
#Entity
#Table(name = "DORM_ROOMS")
public class DormRooms implements Serializable {
#EmbeddedId
private DormRoomsPK dormRoomsPK;
...
}
Assume that the database schema is already defined and created. In particular, CAMPUS+STUDENT_ID is a PK for STUDENTS and CAMPUS+STUDENT_ID is a FK for DORM_ROOMS that serves as the PK in that table. At this point I can successfully insert a row into STUDENTS and a row into DORM_ROOMS. I can also retrieve any student from STUDENTS even if the student does not have a dorm room. However, I have not yet "informed" Hibernate about the relationship between the two tables. That is where I am confused.
I tried to "relate" the two tables by using a "JOIN" annotation but I discovered that this causes any attempt to fetch a student that has no dorm room to return an empty result set. I suppose that makes since if "JOIN" states that the tables are to always be viewed as joined then joining a student having no dorm room with no matching rows in the DORM_ROOMS table would result in an empty result set.
Since using a "JOIN" annotation doesn't work, how do I modify my code to describe the relationship between the two tables but still allow me to fetch students that have no matching dorm rooms?
Thank you.
It sounds like you are looking for the #OneToOne annotation, which also has the ability to specify if the relationship is optional. There are some examples described in the JBoss JPA 2.1 docs, here is one of them.
Example 3: One-to-one association from an embeddable class to another entity.
#Entity
public class Employee {
#Id int id;
#Embedded LocationDetails location;
...
}
#Embeddable
public class LocationDetails {
int officeNumber;
#OneToOne ParkingSpot parkingSpot;
...
}
#Entity
public class ParkingSpot {
#Id int id;
String garage;
#OneToOne(mappedBy="location.parkingSpot") Employee assignedTo;
}
Found the problem! I discovered that in a #OneToOne relationship with a composite key, using a separate FK class to manage the composite key in both entities causes the error. The problem is shown in my original posting where I define and use StudentsPK and DormRoomsPK! Once I changed to use a single "PK" class instead of these two my problem was eliminated. (This doesn't appear to be a well documented requirement!)