What I currently have:
#Entity
public class Payment {
#Id #GeneratedValue
private long id;
#Column(unique = true)
private Date period; // Only used for year and month
...
}
#Entity
public class Department {
#Id #GeneratedValue
private long id;
...
}
The Payment entity just holds default payments that need to be paid by all departments only once pear year and month. There is no relationship needed for between them as all Departments pay all Payments.
What I want to achieve:
I want to distinguish between the currently shared payments and some other Department specific payments. So a Department will be able to choose to use all the shared payments (as it is currently designed) or define its own payments and not use any of the other ones.
The company Payments should keep working in the same way and I have to make sure that the Department payments are unique for each department too.
In OOP terms, I think I need to model any of the following options:
Probably the first one would be more appropriate.
Note I can't change the way any entity is currently identified. However, I can add uniqueness on any other fields.
Questions:
What would be the appropriate way to do this in JPA2?
Is a Payment hierarchy the way to go? How should it be mapped to make sure the unique
fields don't collide?
Is there any way to avoid the hierarchy?
I think the scenario does require a relationship:
#Entity
public class Payment {
#Id #GeneratedValue
private long id;
#Column(unique = true)
private Date period; // Only used for year and month
#ManyToOne
private Department department;
}
This would allow any type of payment to be created for any department. As far as default payments for a department, I think that is outside the responsibility of the ORM and should be handled within the business logic.
If I understood you correctly, you need to achieve uniqueness per department. It's possible using compound id.
Some points:
if you want to use compound keys(period+department_id) you have to set them both and your default payments should have 1 common fake Department to which all default payments will belong to.
In general case I would follow Kevin's approach. It's easy and less error-prone. Anyway you decide.
#Entity
public class Payment implements Serializable {
#EmbeddedId
private Period period;
}
#Embeddable
public class Period implements Serializable {
#ManyToOne
private Department department;
private Date period;
}
#Entity
public class Department implements Serializable {
#Id#GeneratedValue
private long id;
#OneToMany
private List<Payment> payments = new ArrayList<Payment>();
}
I haven't been able to keep the PK in my Payment entity and also maintain a unique index on both Period and Department. In DB terms I'm looking for these:
Payment(ID, Period, FK_Department)
And this table should be added a unique index in Period and FK_Department that would allow nulls in FK_Department (as you can see a compound PK is not an option here for that reason and because I need to keep the same PK structure used). With that table, all Payments with null FK_Department value will be the generic/default/company payments while the ones with a non-null FK_Department will be the ones that a particular department has assigned, so it will use those instead of the company ones.
Due to my lack of knowledge of JPA I couldn't manage to replicate this schema. However, I could create a similarly functional schema. This is the best I came up with so far:
With its obviously awful period duplication I can manage to create two unique index for each table: one for the Period of the CompanyPayment entity and one for the Period and Department pair of the DepartmentPayment entity:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Payment {
#Id #GeneratedValue
private long id;
...
}
#Entity
public class CompanyPayment extends Payment {
#Column(unique = true)
public Date period;
...
}
#Entity
#Table(uniqueConstraints =
#UniqueConstraint(columnNames = { "period", "department_id" })
)
public class DepartmentPayment extends Payment {
public Date period;
#ManyToOne(optional = false)
#JoinColumn(name = "department_id")
private Department department;
...
}
I will be using this solution for now but I'm open to any other better solution.
Related
I was given this assignment, just for practice, it became very long and challenging, but it has taught me a lot, on lambdas and JPA mainly.
It is a basic Rest API, which is used to create Hotels, Rooms, Guests, Reservations, types of guests, types of rooms, etc.
My initial problem was learning about JPA relations, OneToOne, OneToMany, etc., unidirectional, bidirectional, and what not.
I'm also using PostgreSQL, using "sping.jpa.hibernate.ddl-auto=create-drop(or update)", change as needed, when I want to recreate the DB for whatever reason.
So I'm very happy and excited using my new #Annotations to relate my Entities, and fetch back lists of whatever information I needed, came across multiple problems, read many many questions here, solved my problems, but now I have come across a new problem, but then, started questioning my approach, maybe I should not leave everything to JPA.
Let me show you what I mean. I'm going to keep my classes short to show only relevant information.
I have my reservation entity.
#Data
#Entity
#Table(name = "reservation")
public class Reservation {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "guest", referencedColumnName = "id")
#JsonManagedReference
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
private Guest guest;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "room", referencedColumnName = "id")
private Room room;
#ManyToMany(fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#JoinTable(name = "reservation_rooms",
joinColumns = { #JoinColumn(name = "reservation_id" )},
inverseJoinColumns = { #JoinColumn(name = "room_id") }
)
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
private List<ReservationRoom> roomList = new ArrayList<>();
private LocalDate start_date;
private LocalDate end_date;
private Boolean check_in;
private Boolean check_out;
public void addRoom(Room room) {
this.roomList.add(room);
}
public void removeRoom(Long id) {
Room room = this.roomList.stream().filter(g -> g.getId() == id).findFirst().orElse(null);
if (room != null) {
this.roomList.remove(room);
}
}
}
This is my Room entity.
#Data
#Entity
#Table(name = "room")
public class Room {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String description;
private Integer floor;
#JsonProperty("max_guests")
private Integer maxGuests;
#ManyToOne(fetch = FetchType.LAZY)
#JsonBackReference
private Hotel hotel;
#ManyToOne(fetch = FetchType.LAZY)
#JsonProperty("type")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
private RoomType roomType;
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Room)) {
return false;
}
return id != null && id.equals(((Room) o).getId());
}
#Override
public int hashCode() {
return getClass().hashCode();
}
}
And this is my Guest entity.
#Data
#Entity
#Table(name = "guest")
public class Guest {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String first_name;
private String last_name;
private String email;
#ManyToOne(fetch = FetchType.LAZY)
#JsonProperty("type")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
private GuestType guest_type;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "guestList"
)
#JsonBackReference
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
private List<Reservation> reservationList = new ArrayList<>();
public Guest(){}
public Guest(Long id) {
this.id = id;
}
public List<Reservation> getReservationList() {
return reservationList;
}
public void setReservationList(List<Reservation> reservationList) {
this.reservationList = reservationList;
}
}
At the beginning a reservation could only have 1 room, but the requirement changed and it can have multiple rooms now. So now, the guest list needs to be linked to the room linked to the reservation, and not directly to the reservation. (I know I have a Guest and a Room, and also the List of both, this is because I'm using the single Guest as the name for the reservation, and the single Room, as the "Main" room, but don't mind that please).
Letting JPA aside, because every challenge I have faced I would ask my self "how to do it JPAish?", and then research how to do it with JPA (that's how I learned about the #ManyToMany, etc. annotations).
What I would do is just create a new table, to relate the reservations to the room (which is already done in my entities with JPA), and then add also de guest id.
So, this new table, would have a PK with reservation_id, room_id and guest_id. Very easy, then create my Reservation model, which have a List of Room, and this Room model, would have a List of Guest. Easy.
But I don't want to add a List of Guest in my current Room entity, because I have an endpoint and maybe a couple of other functions, which retrieves my Room entity, and I don't want to add a List of Guest, because as the time passes, this list would grow bigger and bigger, and it is information you don't need to be passing around.
So I did some research and found that I can extend my entity with #Inheritance or #MappedSuperclass, and I could create maybe a Reservation_Room model, which includes a List of Guest and add a List of Reservation_Room instead of a List of Room in my Reservation Entity, which I really wouldn't know if it is even possible.
Having said that, and before I keep researching and start making modifications to my code, it got me wondering, if this would be the right approach? Or if I'm forcing JPA too much on this? What would be the best approach for this? Can a 3 id relation table be easily implemented/mapped on JPA?
The main goal would be to have my Room entity exposed as it is, but when a Room is added to a Reservation, this Room would also have a List of Guest. Can I do this JPAish? Or should I create a new model and fill with the information as needed? This wouldn't exempt me from creating my 3 ids table.
Based on what you wrote here, I think you might be at a point where you are realizing that the persistence model doesn't always match the presentation model, which you use in your HTTP endpoints. This is usually the point where people discover DTOs, which you also seem to have heard of.
DTOs should be adapted/created to the needs of the representation of an endpoint. If you don't want to expose certain state, then simply don't declare a getter/field for that data in a DTO. The persistence model should simply be designed in a way, so that you can persist and query data the way you need it. Translation between DTOs and entities is a separate thing, for which I can only recommend you to give Blaze-Persistence Entity Views a try.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Reservation.class)
public interface ReservationDto {
#IdMapping
Long getId();
GuestDto getGuest();
List<RoomDto> getRooms();
}
#EntityView(Guest.class)
public interface GuestDto {
#IdMapping
Long getId();
String getName();
}
#EntityView(Room.class)
public interface RoomDto {
#IdMapping
Long getId();
String getName();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ReservationDto a = entityViewManager.find(entityManager, ReservationDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<ReservationDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
I would say that you need to add a layer between persistence and the endpoints. So, you will have Controllers/Services/Repositories (in the Spring world). You should use entities as return type from Repositories (so used them in Services as well), but return DTOs to Controllers. In this way, you will decouple any modification that you do between them (e.g. you may lose interest to return a field stored in an entity, or you may want to add more information to the dto from other sources).
In this particular case, I would create 4 tables: Reservations, Guests, Rooms, GuestsForReservation.
Guests will contain id + guests data (name, phone number, etc)
Rooms will contain id + room data
GuestsForReservation will contain id + reservationId + guestId (so you can get the list of guests for each reservation). FK for reservationId and guestId, PK for synthetic id mentioned.
Reservations will contain id (synthetic), room id, date from, date to, potentially main guest id (it could be the person paying the bill, if it makes sense for you). No link to the GuestForReservation table, or you can have a list of GuestForReservation if you need to.
When you want to reserve a room, you have a ReservationRequest object, which will go to the ReservationService, here you are going to query the ReservationRepository by roomId and dates. If nothing is returned, you create the various entities and persist them in ReservationRepository and GuestForReservation repository.
By using the service and the combination of various repositories, you should be able to get all the information that you need (list of guests per room, list of guests per date, etc). At the service level, you then map the data you need to a DTO and pass it to the controller (in the format that you need), or even to other services (depending on your needs).
For what concern the mapping between entities and DTOs, there are different options, you could simply create a Component called ReservationMapper (for example) and do it yourself (take an entity and build a DTO with what you need); implements Converter from the Springframework; use MapStruct (cumbersome in my opinion); etc.
If you want to represent in JPA an id made of multiple columns, usually #Embeddable classes are used (you should mark them as EmbeddedId when you use them), you can google them for more info.
The strategy I'm taking to implementing a maker-checker scenario is through using multiple tables. Currently, I'm using Hibernate 4.2 (annotations). The following scenario is what I would like to achieve. However, I'm having problems with the multi-level inheritance.
The basic idea is that there are two tables (pending and approved). When an add() occurs, the entry is inserted into the pending table. When that entry is approved, it is removed from the pending table and inserted into the approved table.
Policy (the policy)
|
+ -- Pending (maker information)
|
+ -- Approved (checker information)
So, class Policy is the class that defines the necessary fields for a policy. To keep this post shorter, the fields are not be shown.
#MappedSuperclass
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) // problem
public abstract class Policy { ... }
The Pending class is for the newly-added Policy that is awaiting approval and it has information on the maker/adder.
#Entity
#Table(name = "pending")
public class Pending extends Policy {
#Column(name = "adder", ...)
private String adder;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "time_added", ...)
private Date timeAdded;
}
The Approved class is for approved entities and it contains additional information on the approver in addition to the information from the Pending class.
#Entity
#Table(name = "approved")
public class Approved extends Pending {
#Column(name = "approver", ...)
private String approver;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "time_approved", ...)
private Date timeApproved;
}
My first thought was to try TABLE_PER_CLASS. However, it resulted in the following runtime error: org.hibernate.MappingException: Cannot use identity column key generation with <union-subclass> mapping for: .... The solution for this is to modify the base class #GeneratedValue(strategy = GenerationType.IDENTITY) to #GeneratedValue(strategy = GenerationType.TABLE). However, modifying that class is beyond my scope as it is shared across multiple projects.
Just for the heck of it, I tried the other two strategies. Obviously, SINGLE_TABLE resulted in one table, with an extra column DTYPE. Not what we wanted. JOINED resulted in two tables, but the approved table has a foreign key to the pending table. Since we wanted to remove an entry from the pending table and move it to the approved table, this would not work for us.
Currently, my solution is to as follows, which is basically copy and paste the code from the Pending class into the Approved class.
#Entity
#Table(name = "approved")
public class Approved extends Policy {
#Column(name = "adder", ...)
private String adder;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "time_added", ...)
private Date timeAdded;
#Column(name = "approver", ...)
private String approver;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "time_approved", ...)
private Date timeApproved;
}
This solution seems counter-intuitive as it duplicates code. Is there a solution that does not require code duplication and keeps the maker-checker process that way it currently works?
After experimenting with the suggested approach by #kostja, I arrived at the following solution.
The maker class encapsulates information pertaining to the maker, which is also an #Embeddable class.
#Embedabble
public class Maker {
#Column(name="maker_id", ...)
private String makerId;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="time_added", ...)
private Date timeAdded;
}
Similarly, the checker class also encapsulates information pertaining to the checker, which is also an #Embeddable class.
#Embedabble
public class Checker {
#Column(name="checker_id", ...)
private String makerId;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="time_approved", ...)
private Date timeApproved;
}
The payload is an #Embeddable class. By making the payload an #Embeddable class, the Maker and Checker can be reused for multiple payloads.
#Embeddable
public class Payload { ... }
For example, given two different payloads that requires maker/checker. One of the payload requires 2 checker.
#Embeddable
public class PayloadA { ... }
#Embeddable
public class PayloadB { ... }
Then we define the following two tables for PayloadA.
#Entity
#Table("a_pending")
public class PendingA {
#Embedded
private PayloadA payload;
#Embedded
private Maker maker;
}
#Entity
#Table("a_approved")
public class ApprovedA {
#Embedded
private PayloadA payload;
#Embedded
private Maker maker;
#Embedded
private Checker checker;
}
Similarly, for PayloadB define two tables. And PayloadB requires two checkers.
#Entity
#Table("b_pending")
public class PendingB {
#Embedded
private PayloadB payload;
#Embedded
private Maker maker;
}
#Entity
#Table("b_approved")
public class ApprovedB {
#Embedded
private PayloadB payload;
#Embedded
private Maker maker;
#Embedded
#AttributeOverrides(value = {
#AttributeOverride(name="checkerId",column="checker1_id"),
#AttributeOverride(name="timeApproved",column="checker1_time_approved"),
})
private Checker checker1;
#Embedded
#AttributeOverrides(value = {
#AttributeOverride(name="checkerId",column="checker2_id"),
#AttributeOverride(name="timeApproved",column="checker2_time_approved"),
})
private Checker checker2;
}
I hope this solution should be general and flexible enough.
I would use a different approach for this. I assume Policy is your entity - the one carrying the real payload. You would like to add some metadata to it. Inheritance does not look as a good fit for this to me. A Pending is not an Policy and an Approved is not a Pending.
Instead of inheritance, I would model the metadata as separate, unrelated entities and create 1-1 relationships to the payload entity. Or many-to-one, if you need multiple approvals.
This way, you have a better decoupled data model and a more normalized DB structure. This gives you more flexibility. You can have single or multiple approvals, change the Approval model without changing the payload entity, and have a better focused payload entity, unburdened by metadata.
The entities could look like this:
#Entity
public class Policy{
#OneToOne
private Creation creation;
#OneToMany(mappedBy="policy")
private List<Approval> approvals;
...
}
Creation:
#Entity
public class Creation{
#OneToOne
private Policy policy;
private String creator;
#Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
...
}
Approval:
#Entity
public class Approval {
#ManyToOne
private Policy policy;
private String approver;
#Temporal(TemporalType.TIMESTAMP)
private Date approvedAt;
..
}
I'm trying to write a hibernate adapter for an old database schema. This schema does not have a dedicated id column, but uses about three other columns to join data.
On some tables, I need to use coalesce. This is what I came up with so far:
About the definition:
A car can have elements, assigned by the car's user or by the car's group of users.
If FORIGN_ELEMENT holds a user's name, definition will be 'u'
If FORIGN_ELEMENT holds a group's name, definition will be 'g'
This also means, one table (CAR_TO_ELEMENT) is misused to map cars to elements and cargroups to elements. I defined a superclass CarElement and subclasses CarUserElement and CarGroupElement.
state is either "active" or an uninteresting string
I set definitition and state elsewhere, we do not need to worry about this.
Use DEP_NR on the join table. If it's zero, use USR_DEP_NR. I did this with COALESCE(NULLIF()) successfully in native SQL and want to achieve the same in Hibernate with Pojos.
Okay, here we go with the code:
#Entity
#Table(name="CAR")
public class Car extends TableEntry implements Serializable {
#Id
#Column(name="DEP_NR")
private int depnr;
#Id
#Column(name="USER_NAME")
#Type(type="TrimmedString")
private String username;
#ManyToOne(fetch = FetchType.EAGER, targetEntity=CarGroup.class)
#JoinColumns(value={
#JoinColumn(name="GROUP_NAME"),
#JoinColumn(name="DEP_NR"),
#JoinColumn(name="state"),
})
private CarGroup group;
#OneToMany(fetch=FetchType.EAGER, targetEntity=CarUserElement.class, mappedBy="car")
private Set<CarUserElement> elements;
}
#Entity
#Table(name="CAR_GROUP")
public class CarGroup extends TableEntry implements Serializable {
#Id
#Column(name="DEP_NR")
private int depnr;
#Id
#Column(name="GROUP_NAME")
#Type(type="TrimmedString")
private String group;
#ManyToOne(fetch = FetchType.EAGER, targetEntity=Car.class)
#JoinColumns(value={
#JoinColumn(name="GROUP_NAME"),
#JoinColumn(name="DEP_NR"),
#JoinColumn(name="state"),
})
private Set<Car> cars;
#OneToMany(fetch=FetchType.EAGER, targetEntity=CarGroupElement.class, mappedBy="car")
private Set<CarGroupElement> elements;
}
#MappedSuperclass
public class CarElement extends TableEntry {
#Id
#ManyToOne(fetch = FetchType.EAGER, targetEntity=Element.class)
#JoinColumns(value={
#JoinColumn(name="ELEMENT_NAME"),
#JoinColumn(name="state"),
})
private Element element;
}
#Entity
#Table(name="CAR_TO_ELEMENT")
public class CarUserElement extends CarElement {
#Id
#Column(name="DEFINITION")
private char definition;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(formula=#JoinFormula(value="COALESCE(NULLIF(DEP_NR, 0), USR_DEP_NR)", referencedColumnName="DEP_NR")),
#JoinColumnOrFormula(column=#JoinColumn(name="FORIGN_ELEMENT", referencedColumnName="USER_NAME")),
#JoinColumnOrFormula(column=#JoinColumn(name="STATE", referencedColumnName="STATE"))
})
private Car car;
}
#Entity
#Table(name="CAR_TO_ELEMENT")
public class CarGroupElement extends CarElement {
#Id
#Column(name="DEFINITION")
private char definition;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(formula=#JoinFormula(value="COALESCE(NULLIF(DEP_NR, 0), USR_DEP_NR)", referencedColumnName="DEP_NR")),
#JoinColumnOrFormula(column=#JoinColumn(name="FORIGN_ELEMENT", referencedColumnName="GROUP_NAME")),
#JoinColumnOrFormula(column=#JoinColumn(name="STATE", referencedColumnName="STATE"))
})
private Car car;
}
I tried all available versions of hibernate (from 3.5.1 [first version with #JoinColumnsOrFormulas] up to 4.x.x), but I always get this error:
Exception in thread "main" java.lang.ClassCastException: org.hibernate.mapping.Formula cannot be cast to org.hibernate.mapping.Column
at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:351)
at org.hibernate.cfg.annotations.CollectionBinder.bindCollectionSecondPass(CollectionBinder.java:1338)
at org.hibernate.cfg.annotations.CollectionBinder.bindOneToManySecondPass(CollectionBinder.java:791)
at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:719)
at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:668)
at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:66)
at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1597)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1355)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1737)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1788)
Other hibernate users seem to have the same problem: They can't get it working with any version, see this thread and other stackoverflow questions:
https://forum.hibernate.org/viewtopic.php?f=1&t=1010559
To be more complete, here's my TrimmedString Class:
https://forum.hibernate.org/viewtopic.php?p=2191674&sid=049b85950db50a8bd145f9dac49a5f6e#p2191674
Thanks in advance!
PS: It works with joining just these three colulmns with just one DEP-NR-Column (i.e. either DEP_NR OR USR_DEP_NR using just #JoinColumns). But I need this coalesce(nullif()).
I ran into a similar problem, and it seems that the issue is that you are using a #Formula inside an #Id. Hibernate wants Ids to be insertable, and Formulas are read-only.
In my case I was able to work around the problem by making the individual columns Id properties on their own, and making the joined object a separate property. I don't know if this would work in your case since you're using two different columns in your formula, but if so your code might look something like:
#Entity
#Table(name="CAR_TO_ELEMENT")
public class CarUserElement extends CarElement {
#Id
#Column(name="DEFINITION")
private char definition;
#Id
#Column(name="DEP_NR")
private Integer depNr;
#Id
#Column(name="USR_DEP_NR")
private Integer usrDepNr;
#Id
#Column(name="FORIGN_ELEMENT")
private String userName;
#Id
#Column(name="STATE")
private String state;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(formula=#JoinFormula(value="COALESCE(NULLIF(DEP_NR, 0), USR_DEP_NR)", referencedColumnName="DEP_NR")),
#JoinColumnOrFormula(column=#JoinColumn(name="FORIGN_ELEMENT", referencedColumnName="USER_NAME", insertable = false, updatable = false)),
#JoinColumnOrFormula(column=#JoinColumn(name="STATE", referencedColumnName="STATE", insertable = false, updatable = false))
})
private Car car;
}
Join formulas are very fragile in Hibernate for the time being; I always had a difficult time to get them work properly.
The workaround that helped me often was to create database views which exposed the proper columns (including foreign keys that don't exist in the original tables). Then I mapped the entities to the views using classing Hibernate/JPA mappings.
Sometimes there are redundant joins in the generated SQL when using such entities, but the database optimizes such queries in most cases so that the execution plan is optimal anyway.
Another approach could be using #Subselects, which are some kind of Hibernate views, but I expect them to be less performant than the classic database views.
I ran into the cast exception as well and I'm on Hibernate 5.x.
Until Hibernate dedicates time to fix the issue, I found that while this guy's approach may not be cleanest (he even eludes to that fact!), it works.
You just need to add the #Column mappings (and get/set methods) to your association table objects that are returning null and manually set the values when you populate the relation data. Simple but effective!
This is in continuation with this question I asked a yesterday. After going through various resources and consulting people, I was not able to find any JPA annotations supporting API, for mapping units of measurement. So, I decided to go with creating it myself.
Based on various patterns on Observations and Measurements described by Martin Fowler in his book - Analysis Patterns: Reusable Object Models, I tried to create a basic implementation to meet my needs. I've created two entities, Unit and Quantity, as below: -
Unit entity: -
#Entity
#Table(name = "unit")
public class Unit {
#Id
#Column(name = "symbol")
private String symbol;
#Column(name = "name")
private String name;
#Column(name = "multiplier")
private Number multiplier;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "symbol")
private Unit baseUnit;
public Unit() {
}
public Unit(String symbol, String name, Number multiplier, Unit baseUnit) {
this.symbol = symbol;
this.name = name;
this.multiplier = multiplier;
this.baseUnit = baseUnit;
}
/** Getters and Setters **/
}
Quantity Entity: -
#Entity
#Table(name = "quantity")
public class Quantity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int quantityId;
private Number amount;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "unit")
private Unit unit;
public Quantity() {
}
public Quantity(Number amount, Unit unit) {
this.amount = amount;
this.unit = unit;
}
/** Getters and Setters **/
}
And in Item entity, I'm having a reference of Quantity entity, to represent my unitOfIssue and unitOfPurchase. And then, wherever I've to deal with some quantity, I'll have this Quantity reference.
Now, here's the problem I'm facing now. Since Quantity class here will be an entity. So, we have to map it to a DB Table. So, the caveat here is, everytime I want to add some quantity, or update the quantity, an entry will go in the Quantity table first, and then it will be updated in the Item table. I think, the problem is pretty clear. We would have huge entry in the Quantity table, which clearly shows a bad design.
Can someone give an insight on the approach and what options I've while I implement this pattern in JPA? And how can I solve the problem?
I would recommend this instead. Since units of measure are unlikely to change often, it is worth building them into the code. That way you can meaningfully use the values in the program itself.
It also makes the database more cohesive if it will ever be used anywhere else. You can also extend Quantity to be things LinearQuantity, ArealQuantity, VolumetricQuantity (etc.) to make sure someone isn't trying to buy 30 feet of oil.
#Embeddable
public class Quantity{
public enum Unit {FEET,METERS,INCHES,MM}
#Enumerated( value = EnumType.STRING)
private Unit unit;
private Number amount;
public Quantity() {
}
}
#Entity
Public Class PurchaseOrder
{
#Embedded
#AttributeOverrides({
#AttributeOverride(name="unit", column=#Column(name="UNIT")),
#AttributeOverride(name="amount", column=#Column(name="AMOUNT"))
})
private Quantity quantity;
....
}
You need a master table of units which will have all the units as they are fixed so you can create simple script to populate it.
For Quantity I will not recommened a seperate entity it can be property to transaction table which can simply establish relation between Item its quantity and unit and you can do that with #OneToOne with Unit Entity.
Here is the sample example
#Entity
Public Class PurchaseOrder
{
#OneToOne
private Unit unitOfPurchase;
#OneToOne
private Unit unitOfIsuse;
-- Quanity number here ---
}
You should use Cascade very carefully for master tables.
Check out this JPA library for the popular JScience project: JScience-JPA
Based on similar support for Java Monetary types (JSR 354) we also plan to add something similar for JSR 363
I am trying to figure out the best way to accomplish a relationship in hibernate. I have a Customer object. Each customer has a technical contact, a billing contact, and a sales contact. Each type of contact has the exact same data structure (phone, email, address, etc).
My first thought was to create a Contact table, and then have three columns in the Customer table - sales_contact, billing_contact, technical_contact. That would make three distinct foreign key one-to-one relationships between the same two tables. However, I have found that this is very difficult to map in Hibernate, at least using annotations.
Another thought was to make it a many to many relationship, and have a type flag in the mapping table. So, any Customer can have multiple Contacts (though no more than three, in this case) and any Contact can belong to multiple Customers. I was not sure how to map that one either, though. Would tere be a type field on the map table? Would this attribute show up on the Contact java model object? Would the Customer model have a Set of Contact objects. or three different individual Contact objects?
So I am really looking for two things here - 1. What is the best way to implement this in the database, and 2. How do I make Hibernate map that using annotations?
It can be as simple as :
#Entity
public class Contact {
#Id
private String id;
private String phome;
private String email;
private String address;
// ... Getters and Setters
}
#Entity
public class Customer {
#Id
#GeneratedValue
private String id;
#ManyToOne
#JoinColumn(name = "ID")
private Contact billingContact;
#ManyToOne
#JoinColumn(name = "ID")
private Contact salesContact;
#ManyToOne
#JoinColumn(name = "ID")
private Contact technicalContact;
public Customer() {
}
// ... Getters and Setters
}
Now, if you want to make the difference between a BillingContact and a SalesContact at the object level, you can make Contact abstract, and implement it with each type of contact. You will have to annotate the parent class with #Inheritance to specify the inheritance strategy of your choice (SINGLE_TABLE sounds appropriate here, it will use a technical discriminator column - see http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#d0e1168).
How about using #OneToOne and just naming the #JoinColumn differently for each type:
#Entity
public class Contact {
#Id
private String id;
private String phone;
private String email;
private String address;
// ... Getters and Setters
}
#Entity
public class Customer {
#Id
#GeneratedValue
private String id;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="billingContact_ID")
private Contact billingContact;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="salesContact_ID")
private Contact salesContact;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="technicalContact_ID")
private Contact technicalContact;
public Customer() {
}
// ....
}
For each row in Customer table should create three rows in Contact table