#embeddable vs #entity for a mapping a collection - java

This must be quite naive but I have a doubt on when to use #Entity and #Embeddable.
Say I have a User and Notification class.
#Entity
public class User{
//other properties
#onetomany
private List<Notification> notifications;
}
#Entity
public class Notification{
//properties
}
I understand that there will be tables for class User and Notification, and a third table for mapping.
What if I do it like this?
#Entity
public class User {
//other properties
#ElementCollection
private List<Notification> notifications;
}
#Embeddable
public class Notification{
//properties
}
I know this won't create a table for Notification. But I can still store my notification objects. I went through the documentation, but couple of doubts:
Is it based on whether I want to see class B as a seperate table?
Is there a performance difference b/w creating a table and an embeddable object?
What can I not do with embeddable object that I can do with a table other than directly querying the table?
NOTES
For anyone reading this question, this question too might help you.

Is it based on whether I want to see class B as a separate table?
Yes, when you use #Embedded, You embed that #Embeddable entity in #Entity class, which makes it to add columns for embedded entity in same table of #Entity class.
Is there a performance difference b/w creating a table and an embeddable object?
When you use #Embedded, for table creation, one query is required, also for inserting and selecting a row. But if you don't use it, multiple queries are required, hence, use of #Embedded yields more performance, we can say.
What can I not do with embeddable object that I can do with a table other than directly querying the table?
Removing the respective embedded entity may be, but there may be integrity constraint violations for this.

In JPA, there’s a couple ways to create composite key fields. Lets see the method using the #Embeddable annotation.
Let’s start with the Entity class.
#Entity
#Table
public class TraceRecord {
#Id
private TraceRecordPk id;
#Version
#Transient
private int version;
#Column(columnDefinition = "char")
private String durationOfCall;
#Column(columnDefinition = "char")
private String digitsDialed;
#Column(columnDefinition = "char")
private String prefixCalled;
#Column(columnDefinition = "char")
private String areaCodeCalled;
#Column(columnDefinition = "char")
private String numberCalled;
}
This is a pretty simple Entity class with an #Id and #Version field and a few #Column definitions. Without going into too much detail, you’ll see that the #Version field is also annotated #Transient. I’ve done this simply because my table also doesn’t have a column for tracking versions, but my database is journaled, so I’m not too concerned about versioning. You’ll also notice that the #Column fields have a value of “char” set on the columnDefinition attribute. This is because the fields in my table are defined as char and not varchar. If they were varchar, I wouldn’t need to do this since a String maps to a varchar field by default.
The #Id field is what I’m interested in right now. It’s not a standard Java type, but a class I’ve defined myself. Here is that class.
#Embeddable
public class TraceRecordPk implements Serializable {
private static final long serialVersionUID = 1L;
#Temporal(TemporalType.DATE)
#Column
private Date dateOfCall;
#Column(columnDefinition="char")
private String timeOfCall;
#Column(columnDefinition="char")
private String callingParty;
/**
* Constructor that takes values for all 3 members.
*
* #param dateOfCall Date the call was made
* #param timeOfCall Time the call was made
* #param callingParty Extension from which the call originated
*/
public TraceRecordPk(Date dateOfCall, String timeOfCall, String callingParty) {
this.dateOfCall = dateOfCall;
this.timeOfCall = timeOfCall;
this.callingParty = callingParty;
}
}
To make this class capable of being an #Id field on an Entity class, it needs to be annotated with #Embeddable like I mentioned earlier. The 3 fields I’ve selected for my composite key are just normal #Column definitions. Rather than create getters/setters for each field, I’ve simply implemented a constructor that takes values for all 3 fields, making any instance immutable. When annotating a class with #Embeddable, that class will need to implement Serializable. So I’ve added a default serialVersionUID to accomodate.
Now that you have a class created and annotated with #Embeddable, you can now use it as the type for an #Id field in your Entity class. Simple stuff eh.

Related

Batch insert entity with composite key

Having these classes:
public class SomeCompositeKey implements Serializable {
private Long objectAId;
private Long objectBId;
private Long objectCId;
}
//lombok annotations~~
#Entity
#Table(name = "objects_assoc")
#IdClass(SomeCompositeKey.class)
public class ObjectsAssocEntity {
#Id
#ManyToOne
#JoinColumn(name = "object_a_id")
private ObjectAEntity objectA;
#Id
#ManyToOne
#JoinColumn(name = "object_b_id")
private ObjectBEntity objectB;
#Id
#ManyToOne
#JoinColumn(name = "object_c_id")
private ObjectCEntity objectC;
}
I'm fetching references of ObjectsAssocEntity members using EntityManager.getReference() and trying to save few entities at once using saveAll method, and each time hibernate executes select to check wheter entity exists or not and then make single insert in case when it not exists.
Is there any possibility to use batch insert in this case? Or should I try to do this e.g with native query?
As described in docs. The spring default implementation must know if the entity is new or not.
In the case of unique primary key: It's easier for spring determine if it's new because before insert the entity id is null.
In the case of a composite key: The ID values are always filled in before persist. So in this case, for determine if the entity is new or not, you can do one of the following approachs:
Use a #Version property in the entity class. For a new object, version is null. So that's enough.
Implement Persistable interface
Override the default spring SimpleJpaRepository EntityInformation and registering as a bean.
Suggestion: #Version has several benefits in the concurrency world. So use it.
Spring docs

Is determinePrimaryKeyJoinColumnName() ever used in Hibernate 5 ImplicitNamingStrategy?

I am working on hibernate 5 and implemented the ImplicitNamingStrategy interface. Among other methods, there are two methods called determinePrimaryKeyJoinColumnName(...) and determineJoinColumnName(...). In the java doc, it says about determinePrimaryKeyJoinColumnName:
Determine the column name related to {#link javax.persistence.PrimaryKeyJoinColumn}. In
* {#code hbm.xml} terms, this would be a {#code } defined for a {#code }
* or a {#code } (others?)
I annotated my joins with PrimaryKeyJoinColumn and the code works, however the names never get routed through determinePrimaryKeyJoinColumnName(...) but through determineJoinColumnName(...).
Am I wrong in believing this is a bug?
#PrimaryKeyJoinColumn can be used like #JoinColumn only for the #OneToOne mapping. In such situation an additional join column is not used and, of course, the name of such "not existing column" is not generated.
#PrimaryKeyJoinColumn can be used for an inheritance too. For an example
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "someDiscriminator")
public class Customer {
#Id
#GeneratedValue
private Long customerPid;
#Column
private String customerName;
}
#Entity
#PrimaryKeyJoinColumn(name = "xxxYY")
public class ValuedCustomer extends Customer {
#Column
private String valuedCustomerName;
}
Hibernate will use xxxYY for a column name. But if you do not specify a name
#Entity
#PrimaryKeyJoinColumn
public class ValuedCustomer extends Customer {
#Column
private String valuedCustomerName;
}
}
Hibernate will not use determinePrimaryKeyJoinColumnName() to generate a name. So, looks like, it is a bug.
Hibernate calls determinePrimaryKeyJoinColumnName() only in one place Ejb3JoinColumn.java#L719. But I can't get in which situations this happens.
I have encountered such problems too, when try to implement an adapter of Hibernate 4 NamingStrategy for Hibernate 5. You can refer ImprovedNamingStrategy for Hibernate 5 for an additional notes.
And my try to implement Hibernate 5 Implicit Naming Strategy.

How to use hibernate to query for an object with a nested object that has a nested collection of objects

Using Hibernate, I need to query a MySQL database for a Post entity that has a one-to-one relationship with a Poll entity that has a one-to-many relationship with an Answer entity. I need the Post object to contain the Poll object and its Poll object to contain its Answer objects. Here's the basic class setup:
Update:
The Post table must not have a primary key column. It is a waste of data. I need to be able to get Post objects from the database using the user_id column. Getting Post objects using the user_id column is the only way it will ever be done, so it makes no sense for me to have a primary key column. So if you're going to provide an answer that provides insight into a solution that solves my problem, please keep those specifications in mind.
Post Class:
#Entity
#Table(name="user_feed")
public class Post implements Serializable {
//id for the user that is meant to receive the post
//*post object is taken from a table that will contain
//*posts for many different users
#Id
#Column(name="user_id")
private long mUserId;
//poll id
#Id
#Column(name="poll_id")
private long mPollId;
//boolean that indicates whether this post is a repost
#Column(name="is_repost")
private boolean mIsRepost;
//date the post was created
#Column(name="date_created")
private Date mDateCreated;
//the poll this post contains
#OneToOne
#JoinColumn(name="poll_id")
private Poll mPoll;
Poll Class:
#Entity
#Table(name="poll")
public class Poll implements Serializable{
//the poll's id
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private long mId;
//id of the user who created the poll
#Column(name="user_id")
private long mUserId;
//the text of the poll's question
#Column(name="question")
private String mQuestion;
//the date the poll was created
#Column(name="date_created")
private Date mDateCreated;
//the answer objects for this poll
#OneToMany
#JoinColumn(name="id")
private List<Answer> mAnswers;
Answer Class:
#Entity
#Table(name="answer")
public class Answer implements Serializable {
//id for a particular answer
//*this is not a necessary value for the application logic, but
//*Hibernate forces me to designate an #Id annotation for every
//*entity, so I created this field and the associated column in
//*the database
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private long mId;
//the answer's text
#Column(name="answer_text")
private String mAnswer;
//the id of the poll to which this answer pertains to
#Column(name="poll_id")
private long mPollId;
***I'm confused about the id for this table. It doesn't make sense for each answer to have a primary key, but Hibernate requires some sort of #Id annotation in the class, so I decided to just create a primary key column in the table for the sake of Hibernate. It's never used. I would like to get rid of it, but there really isn't anything that makes one Answer unique from another for the same poll except for their text at the moment -- it's not necessary for the application logic.
Query I came up with: doesn't work
.
This query was really just me testing to see if I could get a single Post object with all of its nested objects. I knew if I could get one, getting a collection wouldn't be much more of a stretch -- but I can't even get one.
Session session = HibernateUtilities.openSession();
session.beginTransaction();
//29 is a post meant for a particular user and 47 is the id of the
//poll that should be contained in the post
Post post = (Post)session.get(Post.class, new Post(29, 47));
session.getTransaction().commit();
session.close();
//suppose to return the post in JSON format to a client, but it
//doesn't work when I create the one-to-many relationship between
//the poll and it's answers. It only works without the relationship;
//which I've defined in the Poll class
return mGson.toJson(post);
You shouldn't put the primary keys of the relationships as fields of their own (e.g. you don't need both Post.mPoll and Post.mPollId, just use Post.mPoll.getId() if you need it). If I were to address your problem I would by default (we can discuss Post not having an id later) use the following object model (getters omitted for brevity but I would have them on all fields).
#Entity
public class Post {
#Id
#GeneratedValue
private long id;
#OneToOne
private Poll poll;
}
#Entity
public class Poll {
#Id
#GeneratedValue
private long id;
#OneToMany
private List<Answer> answers;
}
#Entity
public class Answer {
#Id
#GeneratedValue
private long id;
}
Start from there and see where it falls apart. If you want an entity to not have any ID then you can use the #Embedded, #Embeddable, and #ElementCollection annotations.
#Embeddable was originally meant for embedding "value" objects (e.g. things like currency, dates, postal addresses, etc.) and as such these objects do not need a primary key and are completely owned by their owning entity.
You reference the embeddable object with the #Embedded annotation (e.g. your User would have an #Embedded reference to the #Embeddable post if it were a one-to-one).
To reference a collection of embeddable objects you use the #ElementCollection annotation. However, members of an #ElementCollection are immutable (can't modify them in the database, have to remove it from the collection and add a new instance) and cannot be lazily loaded. Given the complexity of your Post object I would not personally make it an embedded class (you may want the ability to edit a post someday?) but if you want to it should work.
I say should because I have never had an embeddedable class that references other non-embeddable entities (e.g. your reference to the Poll). Give those things a try and if they don't work then please post exactly what is going wrong.
Solved it myself. All the comments in the below code designate the changes I made to the code I presented in the question and explain why I made them.
Post Class:
#Entity
#Table(name="user_feed")
public class Post implements Serializable {
#Id
#Column(name="user_id")
private long mUserId;
//removed long mPollId
//hibernate is capable of getting the foreign key for a post's
//poll_id column from its poll object -- mPoll
//so i don't have to have a separate field for the id of this post's
//poll
#Column(name="is_repost")
private boolean mIsRepost;
#Column(name="date_created")
private Date mDateCreated;
//made this field part of the composite id instead of long mPollId
//pretty much the same composite key as before just had to alter
//my implementation of Post.equals(Object) to use this poll's id
//instead of this class's mPollId field
//implementing your own .equals(Object) method is necessary when
//creating composite keys as i do with multiple #Id annotations
//i think you also have to implement your own .hashCode() method too
//but the word hash scares me, so I didn't do it
//the code works, so i'm just gonna let it rock
#OneToOne
#JoinColumn(name="poll_id")
private Poll mPoll;
Poll Class:
#Entity
#Table(name="poll")
public class Poll implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private long mId;
#Column(name="user_id")
private long mUserId;
#Column(name="question")
private String mQuestion;
#Column(name="date_created")
private Date mDateCreated;
//removed #JoinColumn -- not completely sure about why it wasn't
//helping, but many of the examples similar to my use case didn't
//use it so I got rid of it
//added mappedBy variable -- still not really sure what it does
//but it works
//and added FetchType.EAGER so everytime a Poll object is loaded
//the answers it's associated with are loaded too
#OneToMany(mappedBy="mPoll", fetch=FetchType.EAGER)
#Cascade({CascadeType.SAVE_UPDATE, CascadeType.REMOVE})
private List<Answer> mAnswers;
Answer Class:
#Entity
#Table(name="answer")
public class Answer implements Serializable {
//turns out having a primary key on the answer table is actually useful
//for the application logic. would you look at that
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private long mId;
#Column(name="answer_text")
private String mAnswer;
//got rid of long mPollId
//it was for the same reason i did in the Post class
//hibernate does the work for me with the mPoll object and the
//annotations i've provided on it
//made the relationship between a poll and its answers bidirectional
//not entirely sure how adding the below annotations to the new
//Poll field fixed my problems, but it did
//i imagine it somehow tells hibernate that the primary key
//for the below object is the foreign key represented by poll_id in the
//database table for this entity
//and making insertable=true enables hibernate to insert that foreign
//key into the appropriate column in the database when this entity
//is saved
//updatable seemed to be necessary
//hibernate complained when it wasn't there
//and nullable was in the helpful examples i found so it was copy and
//pasted along with the rest of the helpful stuff here
//this field can't be nullable anyways so semantically, it makes sense
//for it to be there
#ManyToOne
#JoinColumn(name="poll_id", nullable = false, insertable=true, updatable=false)
private Poll mPoll;
Final functioning query: does work
Session session = HibernateUtilities.openSession();
session.beginTransaction();
List<Post> usersFeed = session.createQuery("select p from Post p where p.mUserId = :userId")
.setString("userId", userId)
.list();
session.getTransaction().commit();
session.close();

How to define unidirectional OneToMany relationship in JPA

I have a following problem with entity mapping in JPA. I have two entities, first one is Lookup and the second is Text which represents translations for entities. Now I need to bound Lookup to the Text but I don't want Text to have reference to Lookup. To make this more complicated, Text does not use its primary key in this relationship but a metacode defined in a TXTHEAD_CODE column.
Lookup.java
#Entity
#Table(name = "DATREG")
public class Lookup implements PersistableEntity {
#Id
#Column(name = "DATREG_META_CODE")
private String metaCode;
#OneToMany
#JoinTable(name="TXT",
joinColumns=#JoinColumn(name="DATREG_META_CODE", referencedColumnName="TXTHEAD_CODE"),
inverseJoinColumns=#JoinColumn(name="DATREG_META_CODE"))
private List<Text> text;
Text.java
#Entity
#Table(name = "TXT")
public class Text {
#Id
#Column(name = "TXT_ID")
private Long id;
#Column(name = "TXTHEAD_CODE")
private String code;
So I have tried this (and few other variations) but with no result. I also can't create join table in the DB and I don't want bound Lookup to my Text class. So can anyone please tell me if there is some other way?
My bible for JPA work is the Java Persistence wikibook. It has a section on unidirectional OneToMany which explains how to do this with a #JoinColumn annotation. In your case, i think you would want:
#OneToMany
#JoinColumn(name="TXTHEAD_CODE")
private Set<Text> text;
I've used a Set rather than a List, because the data itself is not ordered.
The above is using a defaulted referencedColumnName, unlike the example in the wikibook. If that doesn't work, try an explicit one:
#OneToMany
#JoinColumn(name="TXTHEAD_CODE", referencedColumnName="DATREG_META_CODE")
private Set<Text> text;

Can I configure Hibernate/JPA to update an entity record when only non-timestamp fields have been modified?

At the moment I have an Hibernate entity class as follows:
#Entity
#Table(name = "entity")
public class Entity implements Serializable {
private static final long serialVersionUID = 2040757598327793105L;
#Id
#Column
private int id;
#Column
private String data;
#Column(name = "last_modified")
#Temporal(TemporalType.TIMESTAMP)
private Date lastModified;
}
I've found that even when the non-timestamp fields are not modified (i.e. the data field) a call to merge still updates the timestamp. I would like the timestamp to only update when other data fields have changed.
Is there anyway I can prevent calls to merge making a SQL UPDATE when all other data fields are not modified, or do I have to explicitly check for this myself in the code?
Update (thanks to comment):
Since v4 of Hibernate #Entity annotation is deprecated and for allowing dynamic updates you should use #DynamicUpdate(true) (in conjunction with #SelectBeforeUpdate(true))
If you want to prevent unmodified fields to be included in UPDATE queries, add this on your entity:
#org.hibernate.annotations.Entity(dynamicUpdate=true) // update only changed fields
public class ...

Categories

Resources