hibernate mapping for shared model - java

There are 3 entity classes - Item, Seller, Comment. Where,
Item 1 --- * Comment, and Seller 1 --- * Comment
How can I map the class with JPA/Hibernate annotation without adding addition tables?
The tables structure are:
Item(id, description)
Seller(id, name)
Comment(id, entityType, entityKey, message)
where entityType is ITEM or SELLER, the entityKey is either item.id or seller.id .
Right now, I have something like the following:
Update: OneToMany side is OK now, still need to figure out how to make it works in the ManyToOne side.
#Entity
#Table(name = "item_tb")
public class Item {
#Id
#GeneratedValue
private Long id;
#Column(name = "description")
private String description;
*** #OneToMany
*** #JoinColumn(name = "entityKey")
*** #Where(clause = "entityType = 'ITEM'")
private List<Comment> comments;
}
#Entity
#Table(name = "seller_tb")
public class Seller {
#Id
#GeneratedValue
private Long id;
#Column(name = "name")
private String name;
*** #OneToMany
*** #JoinColumn(name = "entityKey")
*** #Where(clause = "entityType = 'SELLER'")
private List<Comment> comments;
}
#Entity
#Table(name = "comment_tb")
public class Comment {
#Id
#GeneratedValue
private Long id;
#Column(name = "entityType")
private String entityType;
#Column(name = "entityKey")
private Integer entityKey;
#Column(name = "message")
private String message;
#ManyToOne
...
private Item item;
#ManyToOne
...
private Seller seller;
}

There are two possibilities I can think of:
[1] Use Hibernate's #Where annotation on each of the #OneToMany mappings. This can be used to restrict the comments to the appropriate type for each collection.
[2] Use Hibernate's inheritance features (table per class hierarchy for the schema as it is). Create two entities SellerComment and ItemComment mapped to the same table and using a discrimiator column (entityType). The the #OneToOmany collections to the relevant types:
There may be issues with [2] which will require use of the #ForceDiscriminator annotation. The approch is outline here: http://www.gmarwaha.com/blog/2009/08/26/hibernate-why-should-i-force-discriminator/

You can use One to Many bidirectional without join table, meaning the Comment table would have a foreign key pointing to the item table. Your relationship would look like
Item
..............
#OneToMany(mappedBy="item")
private List<Comment> comments;
Comment
...............
#ManyToOne
#JoinColumn(name="item_id")
private Item item;
In your code you must set both sides of relations correctly.

Related

How do I map all these tables with Hibernate?

I have a table PATIENT which has some fields. There's also a CONTACT table that has a field called 'patientId' that needs to store PATIENT's ID (which is autogenerated), and a PATIENT_CONTACT table that only relates the two tables.
Now, here comes the tricky part. There are three other tables: CONTACT_ADDRESS, CONTACT_PHONE, CONTACT_EMAIL. A row in CONTACT will have the same ID as one (and only one) of CONTACT_ADDRESS, CONTACT_PHONE and CONTACT EMAIL. How do I get this all to work?
I have tried so many approaches, this is what I have right now:
#Entity
#Table(name = "patient", schema = "patient")
public class PatientEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
//... more fields
#OneToOne
private ContactEmailEntity contactEmailEntity;
#OneToOne
private ContactAddressEntity contactAddressEntity;
#OneToOne
private ContactPhoneEntity contactPhoneEntity;
}
The three CONTACT_* classes are similar and they look like this:
#Table(name = "contact_address", schema = "patient")
public class ContactAddressEntity {
#Id
#Column(name = "id")
private Long id;
// ... more fields
#OneToOne(cascade = {CascadeType.ALL})
#MapsId
private ContactEntity contact;
}
And my CONTACT class looks like this:
#Table(name = "contacto", schema = "paciente")
public class ContactEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
//... more fields
Can you see things that don't look right or could be done better? I get all sorts of errors with every approach. My latest one is:
ERROR: column patientent0_.contact_address_entity_contact_id does not exist
when trying to do a simple patient find. Please, any help is appreciated!

Does anybody know the usage of table attribute in #JoinColumn annotation? Should i give the actual table name or the entity name?

I have a problem in using #ManyToOne relation, because my foreign key and primary key have same name, so Hibernate is considering the one i mapped as the current table's column name.
So i thought i will use table attribute of #JoinColumn annotations, but it seems to be not working. I gave actual table for it's value, but it is not taking. Tried the Class-name as well.
below are the Entities. You can see PRJ_NBR is same in both tables. So when i try to run the project i'm getting error like Cannot find the expected secondary table: no HEAD available
If I remove table attribute from #JoinColumn I'm getting error - Repeated column in mapping for entity: com.example.jpademo.Detail column: PRJ_NBR (should be mapped with insert="false" update="false")
#Entity(name="Head")
#Table(name = "HEAD")
public class QuoteHead {
#Id
#Column(name = "PRJ_NBR")
private Integer projNumber;
#Column(name = "CUS_SYS_ID")
private Integer cusSysId;
#OneToMany(mappedBy = "head", cascade = CascadeType.ALL, orphanRemoval =
true)
private List<Detail> details = new ArrayList<Detail>();
}
#Entity
#Table(name = "DETAIL")
#JsonIgnoreProperties
public class Detail {
#Transient
private Integer projectNumber;
#Transient
private Integer itemNumber;
#Transient
private Integer sequenceNumber;
#ManyToOne
#JoinColumn(name = "PRJ_NBR", table = "HEAD")
private Head head;
#JsonIgnore
#EmbeddedId
private DetailCompositeId id;
}
#Embeddable
public class DetailCompositeId implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "PRJ_NBR")
private Integer projectNumber;
#Column(name = "ITM_NBR")
private Integer itemNum;
#Column(name = "SEQ_NBR")
private Integer sequenceNumber;
}
Remove 'table' and try putting insertable="false" , updatable="false" then I think it should work
You can utilize the #MapsId annotation.
See this post: can someone please explain me #MapsId in hibernate?
You can remove #JoinColumn in your #ManyToOne mapping and replace it with #MapsId, as shown below:
#ManyToOne
#MapsId("projectNumber")
private Head head;

Hibernate mapping join on one column of constants table

I have 2 tables, the first one is quite variable, the second one contains only constants:
USER.ID USER.NAME USER.USER_TYPE (FK on USER_TYPE.ID)
INT VARCHAR(64) INT(1)
----------------------------------
1 Alex 3
2 Jane 1
3 Carl 3
USER_TYPE.ID USER_TYPE.VALUE
INT(1) VARCHAR(64)
------------------------------
1 PENDING
2 REGISTERED
3 BANNED
4 ACTIVE
The foreign key USER.USER_TYPE is required and refering to a primary key USER_TYPE.ID in table USER_TYPE (one-to-one relation). Here is my mapping in Hibernate.
User.java
#Entity
#Table(name = "USER")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "NAME")
private String name;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "USER_TYPE")
private UserType userType;
}
UserType.java
#Entity
#Table(name = "USER_TYPE")
public class UserType {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "VALUE")
private String value;
}
My goal is to keep the enumerated values in the database. How to map UserType's value instead of id to User and validate it? I want to pass the constant VALUE to the String instead of its ID.
private String userType;
The expected result of the first user would be:
User[id=1, name=Alex, userType=Banned]
User[id=2, name=Jane, userType=Pending]
User[id=3, name=Carl, userType=Banned]
My attempt was to use this annotation on definition of table twice with both colums switched
#SecondaryTable(name="USER_TYPE",
pkJoinColumns={#PrimaryKeyJoinColumn(name="ID", referencedColumnName="USER_TYPE")}
)
and get the VALUE with
#Column(table="USER_TYPE", name="VALUE")
private String UserType;
however it leads to the error
Unable to find column with logical name: USER_TYPE in org.hibernate.mapping.Table(USER) and its related supertables and secondary tables
First you need to change the relation from #OneToOne to #ManyToOne as UserType can be used by one or many User and User can have one and one UserType.
Secondly use referencedColumnName which references :
The name of the column referenced by this foreign key column.
So User entity will be:
#Entity
#Table(name = "USER")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "NAME")
private String name;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "USER_TYPE", referencedColumnName = "VALUE")
private UserType userType;
}
In UserType you should apply a unique constraint using #NaturalId to value field + do not provide its setter, to prevent duplicate values as It may lead to inconsistency:
#Entity
#Table(name = "USER_TYPE")
public class UserType {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#NaturalId
#Column(name = "VALUE")
private String value;
}
Hope it solves the issue!
Enumerations could be simpler:
enum UserType {
PENDING,
REGISTERED,
BANNED,
ACTIVE
}
#Entity
#Table(name = "USER")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "NAME")
private String name;
#javax.persistence.Enumerated
private UserType userType;
}
If you really need separated table and #OneToOne relation, you can use #Formula from Hibernate:
#Formula("(select ut.value from user_type ut where ut.ID = USER_TYPE)")
private String userType;
For this really special requirement you could use SecondaryTable annotation.
That is, you don't need UserType entity, but declare attribute userType as String in User entity with column mapping to the secondary table "USER_TYPE".
First of all, I suggest you use ManyToOne relation. and Not CascadeType.ALL if you are not planning update or delete on USER_TYPE table.
If you do not need adding new UserTypes frequently use enum for it. It will just work as you want.
Second solution: As long as fetch = FetchType.EAGER you can add A transient field and return value of UserType in getter.

JPA fetch from multiple tables to single POJO with named query

I have been trying to fetch data from 2 different tables to a single entity in JPA but with no result.
The entity that keeps data from two different tables is as below :
#Data #Entity #JsonSnakeCase
public class WareHouse {
#Id
#GeneratedValue
private long id;
#Column(unique = true)
private String fcId;
#Enumerated(EnumType.STRING)
private FCStatus status;
#OneToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER, mappedBy = "fcId")
private List<WorkingHours> workingHours;
#Enumerated(EnumType.STRING)
private IntegrationType integrationType;
}
The other entity WorkingHours is :
#Data
#Entity
#JsonSnakeCase
public class WorkingHours {
#Id
#GeneratedValue
private long id;
private String fcId;
private LocalDate date;
private DateTime start;
private DateTime end;
}
The tables WareHouse and WorkingHours have one-to-many relationship and fcId is the column that joins them.
In my use case, I have to fetch WareHouse details and its WorkingHours in a single entity WareHouse as defined above. How do I achieve this ?
The named query (below) only fetches the WareHouse data and WorkingHours is coming empty. Is the data model wrong ? Or is the query wrong ? (I thought JPA would take care of automatically fetching from the related table when given the annotations OneToMany and FetchType etc.)
<named-query name="searchByFcId">
<query>
<![CDATA[
select f from WareHouse f where f.fcId = :fc_id
]]>
</query>
</named-query>
You can try the following mappings. The JPA 2.0 spec note (11.1.21) notes:
If the referencedColumnName element is missing, the foreign key is assumed to
refer to the primary key of the referenced table.
However it also goes on to note that:
Support for referenced columns that are not primary key columns of the
referenced table is optional. Applications that use such mappings
will not be portable.
So whether or not this works will depend on your provider.
Warehouse:
#Data
#Entity
#JsonSnakeCase
public class WareHouse {
#Id
#GeneratedValue
private long id;
#Column(unique = true)
private String fcId;
#Enumerated(EnumType.STRING)
private FCStatus status;
#OneToMany(mappedBy = "warehouse", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private List<WorkingHours> workingHours;
#Enumerated(EnumType.STRING)
private IntegrationType integrationType;
}
WorkingHours:
#Data
#Entity
#JsonSnakeCase
public class WorkingHours {
#Id
#GeneratedValue
private long id;
#ManyToOne
#JoinColumn(name = "fcId", referencedColumnName="fcid")
private Warehouse warehouse;
private LocalDate date;
private DateTime start;
private DateTime end;
}

How can I mark a foreign key constraint using Hibernate annotations?

I am trying to use Hibernate annotation for writing a model class for my database tables.
I have two tables, each having a primary key User and Question.
#Entity
#Table(name="USER")
public class User
{
#Id
#Column(name="user_id")
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(name="username")
private String username;
// Getter and setter
}
Question Table.
#Entity
#Table(name="QUESTION")
public class Questions extends BaseEntity{
#Id
#Column(name="question_id")
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#Column(name="question_text")
private String question_text;
// Getter and setter
}
And I have one more table, UserAnswer, which has userId and questionId as foreign keys from the above two tables.
But I am unable to find how I can reference these constraints in the UserAnswer table.
#Entity
#Table(name="UserAnswer ")
public class UserAnswer
{
#Column(name="user_id")
private User user;
//#ManyToMany
#Column(name="question_id")
private Questions questions ;
#Column(name="response")
private String response;
// Getter and setter
}
How can I achieve this?
#Column is not the appropriate annotation. You don't want to store a whole User or Question in a column. You want to create an association between the entities. Start by renaming Questions to Question, since an instance represents a single question, and not several ones. Then create the association:
#Entity
#Table(name = "UserAnswer")
public class UserAnswer {
// this entity needs an ID:
#Id
#Column(name="useranswer_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
#ManyToOne
#JoinColumn(name = "question_id")
private Question question;
#Column(name = "response")
private String response;
//getter and setter
}
The Hibernate documentation explains that. Read it. And also read the javadoc of the annotations.
There are many answers and all are correct as well. But unfortunately none of them have a clear explanation.
The following works for a non-primary key mapping as well.
Let's say we have parent table A with column 1
and another table, B, with column 2 which references column 1:
#ManyToOne
#JoinColumn(name = "TableBColumn", referencedColumnName = "TableAColumn")
private TableA session_UserName;
#ManyToOne
#JoinColumn(name = "bok_aut_id", referencedColumnName = "aut_id")
private Author bok_aut_id;
#JoinColumn(name="reference_column_name") annotation can be used above that property or field of class that is being referenced from some other entity.

Categories

Resources