#Entity
public class Record implements Serializable{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#OneToMany(fetch=FetchType.EAGER)
#Fetch(FetchMode.SUBSELECT)
#Cascade(value={CascadeType.SAVE_UPDATE})
private List<Comment> commentList=new ArrayList<Comment>();
}
#Entity
public class Comment implements Serializable{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(columnDefinition="text")
private String content;
}
I have a Record in my Client, and add SOME comments in the list. Use the RMI method: void update(Record reocord).Then at the Server, hibernate save Comments with id=0 into database, and give a generated id.How can I assign these IDs to Comments in the Client? If not, the other time I call update(Record reocord), Comments will be add twice. Maybe: Record update(Record reocord), but I think if Record have large data it's not wise.
I faced this issue. If you are autogenerating the primary key (which can be mostly used for normalizing tables), Create a unique Id yourself based on the existing data. Do remember to override hashcode and equals for comparison purposes.
Related
I have an entity EncodingResult that references three others. I want to find out how to use the repository's findBy() methods to return an entity based on its foreignKey so that I can, for example, make a GET request passing a Video's foreign key as a parameter and return whether or not there is an EncodingResult containing a Video with the given foreignKey.
How would you go about doing this? I tried reading a bit on EntityGraphs and was rather confused. There also doesn't seem to be a great number of content explaining these parts of the framework.
It would be better if you posted the code for your entities, but from your description, I think you have something like this:
#Entity
public class EncodingResult {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany(mappedBy = "encodingResult")
private List<Video> videos=new ArrayList<Video>();
//...boilerplate
}
#Entity
public class Video {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
#ManyToOne
EncodingResult encodingResult;
//...boilerplate
}
So you can define findBy methods in your EncodingResultRepository like so.
public interface EncodingResultRepository extends JpaRespository<EncodingResult, Integer> {
public Optional<EncodingResult> findByVideoName(String name);
public Optional<EncodingResult> findByVideoId(Integer id);
}
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();
Lets say I'm developing a polling app like strawpoll.me. Would it be best to create my poll bean to contain a list of options or options to have a many to one relationship with a single poll? Also, when submitting the form to create a poll, does the database know to assign the foreign key in the OPTION table to the POLL_ID of the poll they belong to automatically? I know JPA makes database interactions simple but I cant find any examples that add objects with a one to many/many to one relationship at the same time.:
#Entity
#Table(name="POLL")
public class Poll implements Serializable{
#Id
#Column(name="POLL_ID")
#GeneratedValue
private int id;
#Column(name="QUESTION_TEXT")
private String question;
#Column(name="CREATED_ON")
private Timestamp dateCreated;
#OneToMany
#JoinColumn(name="OPTION_ID")
private List<Option> options = new ArrayList<Option>();
}
#Entity
#Table(name="OPTION")
public class Option implements Serializable{
#Id
#Column(name="OPTION_ID")
#GeneratedValue
private int id;
#Column(name="OPTION_TEXT")
private String optionText;
#Column(name="NUM_OF_VOTES")
private int numOfVotes;
}
Or set up like this:
#Entity
#Table(name="POLL")
public class Poll implements Serializable{
#Id
#Column(name="POLL_ID")
#GeneratedValue
private int id;
#Column(name="QUESTION_TEXT")
private String question;
#Column(name="CREATED_ON")
private Timestamp dateCreated;
}
#Entity
#Table(name="OPTION")
public class Option implements Serializable{
#Id
#Column(name="OPTION_ID")
#GeneratedValue
private int id;
#ManyToOne
#Column(name="POLL_ID")
private Poll poll;
#Column(name="OPTION_TEXT")
private String optionText;
#Column(name="NUM_OF_VOTES")
private int numOfVotes;
}
(As I think there are room for improvement for the other 2 answers, I am giving mine)
What you are trying to do is actually quite common. It is normally referred as Bi-directional one-to-many relationship. In your case it should look like:
class Poll {
//....
#OneToMany(mappedBy="poll")
private Set<Option> options;
}
class Option {
//...
#ManyToOne
#JoinColumn("poll_id") // I always like to be explicit on naming :P
private Poll poll;
}
This gives you a more intuitive model. It allow you to navigate from both sides (e.g. in HQL).
One confusion is usually: which side Hibernate is going to use to maintain the relationship in DB? In Hibernate, there should be one side "owning" a relationship (what we shown above is one relationship, though bi-directional). If you have #JoinColumn in both side, Hibernate will complain. In our above example, by using mappedBy, Hibernate knows that it is the Option side owning the relationship. You can see the difference if the Poll side and Option side is inconsistent (e.g. You have Poll-1 containing Option-1, but Option-1 pointing to Poll-2, and Poll-2 is not containing Option-1. The result is, the Option table will be referring Poll-2)
Some pitfalls you should be aware. What is described above about "owning relationship" is simply on the ORM aspect. If you do not properly maintain your relationships in your model, your models may become inconsistent and will have problem if you are changing them in same session. It is your obligation to make sure the relationship is consistent.
It must be:
#Entity
#Table(name="POLL")
public class Poll implements Serializable{
#Id
#Column(name="POLL_ID")
#GeneratedValue
private int id;
#Column(name="QUESTION_TEXT")
private String question;
#Column(name="CREATED_ON")
private Timestamp dateCreated;
#OneToMany(mappedBy="poll")
private List<Option> options;
}
#Entity
#Table(name="OPTION")
public class Option implements Serializable{
#Id
#Column(name="OPTION_ID")
#GeneratedValue
private int id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="POLL_ID")
private Poll poll;
#Column(name="OPTION_TEXT")
private String optionText;
#Column(name="NUM_OF_VOTES")
private int numOfVotes;
}
JPA knows what table hooks up to it by entity name. If you have an Option entity, and your Poll entity contains a List<Option>, then JPA knows how to map one to another.
You DO NOT have to manage both sides of the relationship; if you add an Option to your Poll and persist the Poll, the Option will be updated as well. You don't have to go into the Option to set its Poll, so long as you're refreshing the Option from your data source before you use it again.
I prefer to manage the Many-to-One side, as it is less complicated on a case-by-case basis to tell a child entity which parent it belongs to. Otherwise, for example, adding a new entry to a Set<Option> would require the whole Set to be retrieved from the data source (for the purposes of comparing whether the set already contains it), while telling the Option which Poll it belongs to only needs to update one object.
I have the following mapping:
#Entity
public class Satellite implements Serializable, Comparable<Satellite> {
#NotNull #Id
private long id;
.....
#OrderColumn
#OneToMany(mappedBy = "satellite", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<DataModel> dataModel;
}
and a child entity:
#Entity #IdClass(value=DataModelPK.class)
public class DataModel implements Serializable, Comparable<DataModel> {
private static final long serialVersionUID = -3416403014857250990L;
#Id
private int orbit; // related to reference orbit file
private int dataPerOrbit; // in Gbit
#ManyToOne #Id
private Satellite satellite;
}
originally, DataModel was an embeddable entity, but for a better control over the primary key and the underlying structure of the db, I switched to a more traditional model.
The point is, during the loading of the entity now it generate a stack overflow!! I think there is some cyclic loading between those two entities and it got stuck!
I'm thinking to revert everything back to what it was, but I wish to understand why it gives me this error.
You have #IdClass for DataModel specified to be DataModelPK.class but your #Id annotation is on an int field.
This is a problem, it may be causing you stackoverflow but I am not certain.
Update I now see the second #Id annotation so I stand corrected, I will investigate furtuer.
Really like objectify, although still struggling with what is the best way to structure my data in my app. I will be launching a new app soon and do not want to get stuck with a structure that may not function right, or would perform very slowly.
The app will be on the HRD and will have a large number of entity types. For illustrative purposes I will make up some example entities. Suppose the app is for fast food restaurants. Each chain will be an entity(for example McDonalds, Wendy's, etc.). Each specific franchise or location will be an entity as well. Employees, Orders, Menus, Timesheets, and so on will also be entities.
I guess by biggest question is how to setup the relationships between these entities? I have been store relationships by storing the datastore ID as a long in each entity. For example each employee entity would have a long value that is the datastore ID for the location they work at, as well as for which chain they are a member of.
With this structure I can query for all of the orders from a specific restaurant with a statement such as:
Long restaurantId =restaurant.getId();
Query<Order> q=ofy.query(Order.class).filter("location", resturantId);
Just curious if there is any issue with using the datastore/objectify in this manner. Any input would be great! I have been using something similar on a small scale and seems to work fine. Ideally I would like the most efficient structure, and i realize this may take some testing. However once may app is deployed it may be very difficult to change...
#Entity
public class Chain {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String type;
//getters & setters, etc
}
#Entity
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long chain; //which resturant chain the location belongs to (mcdonalds, wendy's, etc)
private String address;
private String owner;
private String phoneNumber;
//getters & setters, etc
}
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long location; //which location the employee works for
private Long chain; //which resturant chain the location belongs to (mcdonalds, wendy's, etc)
private String name;
private String position;
//getters & setters, etc
}
#Entity
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long chain; //which resturant chain the location belongs to (mcdonalds, wendy's, etc)
private Long location;
private Long employee; //employee that took order
private Order order;
private String time;
//getters & setters, etc
}
This is standard practice. Go forth!
Objectify is great - we've been using it for about 6 months and we're very happy with it.
Key<Object> is type safe, Long is not. Using Long is discouraged in the documentation.
Reference: https://github.com/objectify/objectify/wiki/Entities#relationships
I'd encourage you to read through that entire page, it's worth the time. I now have a type-safe structure using Ref<Object> everywhere.