If I write this way
#Id
#OneToOne
#JoinColumn(name="Id")
private Region region;
it says
Composite-id class must implement Serializable: ....InPopulation
I don't want it be composite key, I just want id replaced by Region of appropriate id.
You use the MapsId annotation for that (i have assumed how the #Id is declared):
#Id
private Long id;
#JoinColumn(name = "Id")
#OneToOne
#MapsId
private Region region;
Related
I have this entity with four composite keys. Since Hibernate cannot generate entities with composite keys, I have to do it manually. That's the way I'm trying:
#Entity
#IdClass(ExamRequisitionPK.class)
#Table(name="ExamRequisitions")
#NamedQuery(name="ExamRequisition.findAll", query="SELECT er FROM ExamRequisition er")
public class ExamRequisition implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(unique=true, nullable=false)
private int id;
#Id
#Column(nullable=false)
#OneToOne
#JoinColumn(name="examId", nullable=false, insertable=false, updatable=false)
private Exam exam;
#Id
#Column(nullable=false)
#OneToOne
#JoinColumn(name="patientId", nullable=false, insertable=false, updatable=false)
private Patient patient;
#Id
#Column(nullable=false)
#OneToOne
#JoinColumn(name="doctorId", nullable=false, insertable=false, updatable=false)
private Doctor doctor;
With this entity mapped, what should I put in the IdClass (ExamREquisitionPK.class)?
I'm newbie with this, it would be great if someone could help me.
Basically, here are the rules:
A dependent entity might have multiple parent entities (i.e., a derived identifier might include multiple foreign keys).
If an entity class has multiple id attributes, then not only must it use an id class, but there must also be a corresponding attribute
of the same name in the id class as each of the id attributes in the
entity.
Id attributes in an entity might be of a simple type, or of an entity type that is the target of a many-to-one or one-to-one
relationship.
If an id attribute in an entity is of a simple type, then the type of the matching attribute in the id class must be of the same
simple type.
If an id attribute in an entity is a relationship, then the type of the matching attribute in the id class is of the same type as the
primary key type of the target entity in the relationship (whether the
primary key type is a simple type, an id class, or an embedded id
class).
I don't see the codes for the Exam, Patient and Doctor entity classes, but I'd like to make assumptions, by giving a sample code:
#Entity
public class Exam {
#Id
private String examId;
...
}
#Entity
public class Patient {
#Id
private Long patientId;
...
}
#Entity
public class Doctor {
#Id
private Integer doctorId;
...
}
Given the above code showing the type of each entity's primary key, here's what you should put in your ExamREquisitionPK.class:
public class ExamREquisitionPK {
private int id; // matches the name of ExamRequisition 1st #Id attribute
private String exam; // matches the name ExamRequisition 2nd #Id attribute but type should match with Exam's PK
private Long patient; // matches the name ExamRequisition 3rd #Id attribute but type should match with Patient's PK
private Integer doctor; // matches the name ExamRequisition 4th #Id attribute but type should match with Doctor's PK
}
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;
...
}
I've got the following classes/relationship (getters & setters not displayed, but present):
public class Contract implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Version
#Column(name = "version")
private Integer version;
private String number;
private String volume;
#OneToMany(cascade=CascadeType.REMOVE)
#JoinTable(joinColumns = #JoinColumn(name = "contract_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "attachment_id", referencedColumnName = "id"))
private List<Attachment> attachments;
}
public class Attachment implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Version
#Column(name = "version")
private Integer version;
#Lob
#Basic(fetch=FetchType.LAZY)
#Column(length=2147483647)
private byte[] contents;
private String name;
}
As per my needs/design, I am using a join table.
If I want to delete an attachment from the Contract, I need to load the Contract, and then loop through all the attachments until I find the one I want to remove and remove it from the list.
Although this is functional, it will require a lot of DB communication. If the list of attachments is long, and contains large contents, it will also require large bandwidth.
Is there any other way I can remove it? If I try to remove the attachment directly (ex: Attachment.findById().delete()), it will fail due to the FK relationship - won't it? (I haven't tried this yet, but I suspect it).
Additionally, if I have a very large list of attachments, iterating through them one by one until I find the correct one is not very efficient either.
Does JPA provide any other/better solution?
There's one workaround solution I know - you can create an entity class for join table.
You will have to give a name to your join table within #JoinTable annotation (name attribute), lets say ContractAttachment. Then you can create entity:
#Entity(name = "ContractAttachment") // note the same name of table
#IdClass(ContractAttachmentId.class)
public class ContractAttachment implements Serializable {
static final long serialVersionUID = 1L;
#Id
#ManyToOne
#JoinColumn(name="contract_id") // same mappings for columns
private Contract contract;
#Id
#ManyToOne(cascade = CascadeType.REMOVE)
#JoinColumn(name="attachment_id") // same mappings for columns
private Attachment attachment;
// you will also have to override equals and hashcode methods here
}
The class ContractAttachmentId should look like:
public class ContractAttachmentId implements Serializable {
private long contract; // note the same fields names
private long attachment;
// this class should also implement hashcode and equals
}
Now you can remove a single entry in join table and even cause attachment object to be deleted too.
ContractAttachment ca = em.createQuery("select ca from ContractAttachment ca " +
"where ca.contract = :contract and ca.attachment = :attachment")
.setParameter("contract", selectedContract)
.setParameter("attachement", selectedAttachment)
.getSingleResult();
em.remove(ca);
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.
#Entity
public class Blobx {
private String name;
private BlobKey blobKey;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key id;
//getters and setters
}
#Entity
public class Userx {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key id;
private String name;
#OneToMany
private List<Blobx> blobs;
//getters and setters
}
while persiting the above Userx entity object i am encountering
java.lang.IllegalStateException: Field "entities.Userx.blobs" contains a persistable object that isnt persistent, but the field doesnt allow cascade-persist!
I think you need to add a cascade attribute so that the JPA provider can cascade persist on the new Blobx added to the blobs. Currently, the JPA provider can't, as reported by the error message. So change it like this (adapt the CascadeType to match your needs):
#OneToMany(cascade = CascadeType.ALL)
private List<Blobx> blobs;