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.
Related
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!
I am just started with hibernate 4 (annotations), I have following three model Classes with One to One and One to Many Relationship.`
#Entity
#Table(name = "USERS",uniqueConstraints = {#UniqueConstraint(columnNames={"EMAIL_ID"})})
public class UserBasicInfo implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="userId")
private Long userId;
#Column(name="EMAIL_ID")
private String emailId;
#Column(name="PASSWORD")
private String password;
#OneToMany(fetch = FetchType.LAZY, mappedBy="userBasicInfo",cascade = CascadeType.ALL)
private Set<UserDeviceInfo> userDeviceInfo;
#OneToOne(mappedBy="userBasicInfo",cascade = CascadeType.ALL)
private UserAdvancedInfo userAdvancedInfo;
///////// Getter setters
}
and
#Entity
#Table(name = "USERS_ADVANCED_DETAILS")
public class UserAdvancedInfo implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="ADVANCED_INFO_ID")
private Integer advancedInfoId;
#Column(name="GENDER")
private Gender gender;
#Column(name="UNIT_OF_MEASUREMENT")
private UnitOfMeasurement unitOfMeasurement;
#Column(name="HEIGHT")
private Double height;
#OneToOne
#JoinColumn(name="userId", insertable = true, updatable = true, nullable = true)
private UserBasicInfo userBasicInfo;
//////////////////////// Getter and Setter ////////////////////////
}
On the basis of above model classes I have two different REST APIs.
In the first API, only basic info will inserted and that is working fine.
But in the second API I have to update On UserAdvancedInfo on the basis of primary key of UserBasicInfo.
I have used update, merge, saveOrUpdate, but instead of updating data these all methods inserting a new row every time.
Kindly help me out.
Thanks,
To update the entity, you should need to set primary key to UserAdvancedInfo object and set UserBasicInfo object with there primary key to UserAdvancedInfo object :
UserAdvancedInfo info = new UserAdvancedInfo();
UserBasicInfo userInfo =new UserBasicInfo();
info.setUserBasicInfo(userInfo);
Hibernate checks for primary key for updation.
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;
I need to have a friendship relationship. I have a friendship class with two primary keys that each is a Member class. I am receiving following exception:
org.hibernate.MappingException: Foreign key (FK_8ynretl1yt1xe3gcvfytrvpq:Friendship [])) must have same number of columns as the referenced primary key (Member [username])
Friendship
#Entity
public class Friendship implements Serializable {
/**
*
*/
private static final long serialVersionUID = -1234656876554786549L;
#Id
#ManyToOne
Member requester;
#Id
#ManyToOne
Member friend;
#Temporal(javax.persistence.TemporalType.DATE)
Date date;
Member
#Entity
public class Member {
#Id
#MapsId
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "username")
Credential credential;
#Column(nullable = false)
String fname;
#Column(nullable = false)
String lname;
#Column(nullable = false)
short gender;
Credential
#Entity
public class Credential {
#Id
#Column(nullable = false, unique = true)
private String username;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private String authority;
#Column(nullable = false)
private boolean enabled;
Putting aside that Member and Credential should implement Serializable if multiple id properties without identifier type are used, your mappings are good, and this seems to be a bug in Hibernate.
Solution 1
I managed to make this work by declaring referencedColumnName in friend and requester associations in Friendship:
#Id
#ManyToOne
#JoinColumn(referencedColumnName = "username")
Member requester;
#Id
#ManyToOne
#JoinColumn(referencedColumnName = "username")
Member friend;
This way we explicitly tell Hibernate which columns the composite id references, so that it does not have to figure it out itself.
Solution 2
The solution 1 made me think of what could be the cause of the bug in Hibernate. It seems that it is somehow affected by the order in which Hibernate processes the entity mappings. If you explicitly declare the referenced column, everything works fine, otherwise it seems that Hibernate does not know all the details about the referenced column at the time it builds the composite key.
So I changed the order in which I add annotated classes to the session factory configuration to:
Credential
Member
Friendship
and then everything worked with your original mappings (after implementing Serializable in Member and Credential).
I added the classes in this order programmatically to the Configuration class, but I assume the same effect could be achieved by specifying this order in the persistence.xml or hibernate.cfg.xml:
<class>Credential</class>
<class>Member</class>
<class>Friendship</class>
Nevertheless, this solution is just for demonstrative purposes (you or someone else can later reorder the classes without keeping this issue in mind), so I suggest using solution 1.
Note
You know your use cases better, but in my personal opinion you should use #IdClass or #EmbeddedId since they are standardized in JPA; multiple id properties without identifier type is a Hibernate specific feature. Besides being able to easier construct the primary key object by which you will search and query the corresponding entities, a dedicated PK object is usually much lighter and offers better performance when serialized, especially if second level cache is enabled.
You have to add a separate ID field to the Member class for the #MapsID annotation to map. Like this:
#Entity
public class Member implements Serializable {
#Id
private String username;
#MapsId
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "username")
Credential credential;
#Column(nullable = false)
String fname;
#Column(nullable = false)
String lname;
#Column(nullable = false)
short gender;
}
You are missing the modeling of the primary key in the Friendship class.
For example:
#Embeddable
public class FriendshipPK implements Serializable
{
#Column(name = "requester_id")
protected String requesterId;
#Column(name = "friend_id")
protected String friendId;
}
Friendship class can now be modified as follows:
#Entity
public class Friendship implements Serializable
{
#EmbeddedId
protected FriendshipPK friendshipId = new FriendshipPK();
#ManyToOne
#MapsId("requesterId")
Member requester;
#ManyToOne
#MapsId("friendId")
Member friend;
#Temporal(javax.persistence.TemporalType.DATE)
Date date;
}
I have updated the Member class slightly:
#Entity
public class Member implements Serializable
{
#Id
protected String memberId;
#MapsId
#OneToOne(optional = false)
#JoinColumn(name = "username")
Credential credential;
#Column(nullable = false)
String fname;
#Column(nullable = false)
String lname;
#Column(nullable = false)
short gender;
}
I removed the cascade from Member class, and created the credentials objects first. But you can change this as fit.
In the Friendship class try specifying the #JoinColumn as well:
#Entity
public class Friendship implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "username")
Member requester;
...
}
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.