I am developing Java EE project with embedded database using JPA ORM. My question is...
When I create #ManyToMany fields in two Entities, I have to create association entity too, or it will do container for me?
Course entity snippet
Public class Course implements Serializable {
...
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#ManyToMany
#JoinTable(name="course_user", joinColumns={#JoinColumn(name="course_id", referencedColumnName="id")},
inverseJoinColumns={#JoinColumn(name="user_id", referencedColumnName="id")})
private List<User> enrolledStudents;
...
User Entity snippet
public class User implements Serializable {
private static final long serialVersionUID = 1L;
...
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#ManyToMany(mappedBy="enrolledStudents")
private List<Course> enrolledCourses;
...
So is this everything I need or not? Thank you for answers!
That will work but see here:
Hibernate Best Practices: Avoiding Many-To-Many and 'exotic' relationships
At some point you will most likely want to save additional info about this relationship (enrollment date for example?) So you may as well create the 'join entity' up front to save refactoring later.
Yes its all you need... if you create the database from the code, you will see three tables... Course, User and Course_User witch has the IDs of Couse and User
Yes, this is sufficient. Look at examples at
http://en.wikibooks.org/wiki/Java_Persistence/ManyToMany
Related
I have created a Spring Boot JPA micro service with a MySQL back end. I have two simple entities in my model, users and skills. These are used to represent the skills base within my organisation. Each user may have many skills and so I have used a one to many join to model this and can successfully assign skills to users. In my database, I can see that a users_skills table has been created by JPA to achieve this.
However, I am now struggling because, for each skill that a user has, an experience level needs to be specified (e.g. basic, advanced, expert) and I am unsure how to achieve this. I'm not sure whether 'levels' should just be an enum type within the Skill entity, or perhaps it should be a separate entity in its own right? Could I configure JPA so that it generates a users_skills_levels table which would represent this three-way relationship? Any advice would be most welcome!
These are my Entity classes: -
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String name;
private String email;
#OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
private Set<Skill> skills = new HashSet<>();
getters and setters
}
#Entity
#Table(name = "skills")
public class Skill {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String name;
getters and setters
}
That's not possible what you try to achieve.
You should create an Entity for the users_skills_levels. E.g. UserSkillLevel This entity will then have a ManyToOne relationship to User and a ManyToOne relationship to Skills plus the attribute level.
The User has a collection of UserSkillLevel and the Skill entity as well.
Please find a more in-depth example here:
https://thoughts-on-java.org/many-relationships-additional-properties/
I want to check if cert entity exist in the database using keys-only queries. So far I'm doing:
Iterable<Key<LikeMW>> liked = ofy().load().type(LikeMW.class).filter("likedObject", postKey).filter("user", userKey).keys();
post.setLiked(liked.iterator().hasNext());
So I have 2 questions:
1 - If I use ".first().now()" after ".keys()", does it switch from "keys-only" or it'll still be a "keys-only" query?
2 - Is there a better way to check if cert entity exist using "keys-only" queries and filter?
Thank you guys!
UPDATING
#Entity
public class LikeMW {
#Id
private Long id;
#JsonIgnore
#Index
#Load
private Ref<UserMW> user;
#JsonIgnore
#Index
private Key likedObject;
...
}
And one of possible liked objects...
#Entity
public class PostMW{
#Id
private Long id;
#JsonIgnore
#Load
private Ref<UserMW> owner;
#JsonIgnore
#Load
private Ref<MediaMW> media;
...
}
The only way to authoritatively look up whether an entity exists is to load it by key. You can certainly do a keys-only query, but it will be eventually consistent and will not guarantee that you do not create duplicates.
Given what you are trying to do, you will almost certainly be better off parenting LikeMW with the user and using the stringified likedObject as the string id. That way you can do a strongly consistent lookup and use transactions.
I'm trying to use Hibernate Search on two Entities, that do not (and must not) share a relation on object-level, however they're connected by a join table that uses their IDs. (legacy)
These are more or less the two Entities:
#Entity
#Indexed
class Person {
#Id
private long id;
#Field
private String name;
....
}
#Entity
#Indexed
class Address {
#Id
private long id;
#Field
private String street;
#Field
private String zip;
....
}
They are connected by their IDs:
#Entity
class Relation {
#Id
private long id;
private long personId;
private long addressId;
}
The goal I'm trying to achieve is finding similar persons that share a similar address via Hibernate Search. This means I'm searching for attributes from both Person and Address.
I guess the easiest way is to "emulate" an #IndexedEmbedded relation which means denormalizing the data and add "street" and "zip" from Address to a Person document. I stumbled upon Hibernate Search Programmatic API, but I'm not sure if that's the right way to go (and how to go on from to there)..
Would this be the proper way of doing things or am I missing something?
If you cannot add this relationship into the model, you will be pretty much out of luck. You are right that you would have to index the Person and corresponding Address data into the same document (this is what #IndexedEmbedded does really). The normal/best way to customize the Document is via a custom (class) bridge. The problem in your case, however, is that you would need access to the current Hibernate Session within the implementation of the custom bridge.
Unless you are using some approach where this Session for example is bound to a ThreadLocal, there won't be a way for you to load the matching Address data for a given Person within the bridge implementation.
I'm using JPA2 with EclipseLink implementation
![Simple table structure][1]
Here are the two tables which I try to map and the JPA annotations.
public class Story implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
Integer id;
#Temporal(TemporalType.TIMESTAMP)
#Column (name="DATE_CREATED")
Date dateCreated;
String title;
String description;
#Column(name="AUTHOR_ID")
Integer authorId;
#Column(name="COUNTRY_ID")
Integer countryId;
private String reviews;
#OneToMany(mappedBy = "story", cascade=CascadeType.ALL)
private List<Tip> tipList;
}
public class Tip implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer id;
private String description;
private Integer vote;
#ManyToOne (cascade=CascadeType.ALL)
#JoinColumn(name="STORY_ID", referencedColumnName="ID")
private Story story;
}
As a simple example I would like to persist a story and some story related tips in the same transaction.
Here is the section of code which does that:
Story newStory = new Story(title, body, ...);
EntityTransaction transaction = em.getTransaction().begin();
boolean completed = storyService.create(newStory);
//The tips are saved as a List<String>. This methods creates the needed List<Tip> from the Strings
List<Tip> tips = TipUtil.getTipList(tipList);
newStory.setTipList(tips)
transaction.commit();
I have no errors and all the entities are persisted in the database. The problem is that in the tip table the story_id field is always NULL. I can imagine that JPA is unable to get the new id from the story table. What's the correct approach here?
LE
In the current state of the code, the Tip entities are persisted but the country ID remains null.
With JPA, it is always recommended to update the relationship on both the sides in a bi-directional relationship. This is to ensure that the data is consistent in your application layer and nothing to do with the database.
However it is mandatory that you update the owning side of the relationship in a bidirectional relationship.
So, setting/not setting
story.setTipList(tips)
is up to you. But if you want the changes to reflect properly in DB then you mush call
tip.setStory(story)
as Tip is the owning side here, as per your code.
Also your code looks incomplete to me. Reasons is,
the entity returned by storyService.create(newStory) is managed but not the newStory. So just setting newStory.setTipList(tips) will not updated the db
Because you need to update the parent link story in each of your child.
The way its is done is to create a addTip(Tip tip) method in your Story class.
This method does :
tip.setStory(this);
tipList.add(tip);
If you don't need bedirectional approach, you can remove the story field in Tip and it will resolve your problem
Remove the
#Column(name = "STORY_ID")
private Integer storyId;
You are already declaring it in #JoinColumn(name="STORY_ID", referencedColumnName="ID")
That is why you are getting the error Multiple writable mappings exist for the field [tip.STORY_ID]
You should not be using PrimaryKeyJoinColumn, just JoinColumn, but having your complete class would help giving a certain answer.
PrimaryKeyJoinColumn would only be used if the story_id was also the id of the Tip (no id in Tip) and there was a duplicate basic mapping for it. It should rarely be used, and is not required in JPA 2.0 anymore as duplicate id mappings are no longer required.
I wanted to know if there is a way to get in a One2Many relationship a field of the One side that is an aggregate of the Many side.
Let's take the following example:
#Entity
public class A {
#Id
private Long id;
#OneToMany (mappedBy="parentA")
private Collection<B> allBs;
// Here I don't know how to Map the latest B by date
private B latestB;
// Acceptable would be to have : private Date latestBDate;
}
#Entity
public class B {
#Id
private Long id;
private Date date;
#ManyToOne (targetEntity=A.class)
private A parentA;
}
My question is how can I make the mapping of the field latestB in the A entity object without doing any de-normalization (not keeping in sync the field with triggers/listeners)?
Perhaps this question gives some answers, but really I don't understand how it can work since I still want to be able to fetch all childs objects.
Thanks for reading/helping.
PS: I use hibernate as ORM/JPA provider, so an Hibernate solution can be provided if no JPA solution exists.
PS2: Or just tell me that I should not do this (with arguments of course) ;-)
I use hibernate as ORM/JPA provider, so an Hibernate solution can be provided if no JPA solution exists.
Implementing the acceptable solution (i.e. fetching a Date for the latest B) would be possible using a #Formula.
#Entity
public class A {
#Id
private Long id;
#OneToMany (mappedBy="parentA")
private Collection<B> allBs;
#Formula("(select max(b.some_date) from B b where b.a_id = id)")
private Date latestBDate;
}
References
Hibernate Annotations Reference Guide
2.4.3.1. Formula
Resources
Hibernate Derived Properties - Performance and Portability
See,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Filtering.2C_Complex_Joins
Basically JPA does not support this, but some JPA providers do.
You could also,
- Make the variable transient and lazy initialize it from the OneToMany, or just provide a get method that searches the OneToMany.
- Define another foreign key to the latest.
- Remove the relationship and just query for the latest.