I'm new to Ebean's world, and I encounter some difficulties to set some relationships between entities.
I have basically two classes, User and Car.
A user can have several cars (so I guess OneToMany) and a car can belongs to one User (so I guess OneToOne).
How can I link these two entities? Here it is what I've done so far
User
#Entity
public class User extends Model{
#Id
#GeneratedValue
public int id;
public String name;
#ManyToMany(cascade=CascadeType.ALL)
public List<Car> car = new ArrayList<Car>();
}
Car
#Entity
public class Car extends Model{
#Id
#GeneratedValue
public int id;
#OneToOne(cascade = CascadeType.ALL)
public User user;
}
And I get the following error
PersistenceException: Error on models.User.car Can not find mappedBy
property [users] in [models.Car]
Can someone explain me clearly how to use annotations the correct way (very poor documentation), and tell me why I get this error?
You guessed wrong :)
Your User should have a #OneToMany relationship with cars so:
#OneToMany(mappedBy = "user", cascade=CascadeType.ALL)
public List<Car> car = new ArrayList<Car>();
while your Car should have a #ManyToOne relationship :
#ManyToOne(cascade = CascadeType.ALL)
public User user;
Take care on the mappedBy property in the #OneToMany annotation: you need to tell Ebean where the foreign key lies in the related class.
User
#Entity
public class User extends Model{
#Id
#GeneratedValue
public int id;
public String name;
#OneToMany(cascade=CascadeType.ALL)
public List<Car> car = new ArrayList<Car>();
}
Car
#Entity
public class Car extends Model{
#Id
#GeneratedValue
public int id;
#ManyToOne(mappedBy="car") //will give you an error
public User user;
}
mappedBy here represents the owner of relation which is important in bidirectional relation.
Think in normal condition can a car exist without the User which owns it means User is the owner in a relation.So in your case User is the owner of relation.Mapped By
But the above code will not work The attribute mappedBy is undefined for the annotation type ManyToOne
In that case #JoinColumn come into picture.Join Column
Related
I have a doubt about how the modeling of my entity would be. Come on, I have a table in the database that serves to save documents from my system, this table has the columns id, fk_id (element foreign key), fk_table (entity name) and file_name (stores the name of my file) .
I did a lot of research before posting my question here, but I didn't find anything related to it, what would my entities, user, patient and doctor?
DB:
id
fk_id
fk_table
file_name
1
21
user
test1.jpg
2
32
doctor
test2.pdf
3
61
user
test10.pdf
4
100
patient
test5.jpg
Class:
public class User{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String LastName;
// What would a one-to-many relationship look like?
}
public class patient{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
// What would a one-to-many relationship look like?
}
You can use #Where. But be aware that #Where is a Hibernate annotation. It's not in the JPA standard.
For example in the User entity: (I assume that your table is mapped to an entity called Document)
#Where( clause = "fk_table = 'user'")
#JoinColumn(name = "fk_id")
#OneToMany
private List<Document> documents = new ArrayList<>( );
The following is based only on standard JPA annotations. The idea is to create an inheritance hierarchy for the documents table. The base is:
#Entity
#Table(name = "XX_DOCUMENT")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "fk_table")
public abstract class BaseDocument {
#Id
#GeneratedValue(strategy=SEQUENCE)
private Long id;
#Column(name = "file_name")
private String fileName;
}
Here we define that all entities extending this will go to the same table, with the fk_table column to discriminate. The entities extending it are defined as follows:
#Entity
#DiscriminatorValue("doctor")
public class DoctorDocument extends BaseDocument {
#ManyToOne
#JoinColumn(name = "fk_id")
private Doctor doctor;
}
#Entity
#DiscriminatorValue("patient")
public class PatientDocument extends BaseDocument {
#ManyToOne
#JoinColumn(name = "fk_id")
private Patient patient;
}
// and so on
The interesting thing is that we are reusing the column fk_id to point to the right table. From a small experiment, Hibernate seems to not have problems with it. I would suggest that you manage the DB creation another way just to be safe.
The Doctor, Patient etc need not have a common base class, e.g.:
#Entity
#Table(name = "XX_DOCTOR")
public class Doctor {
#Id
#GeneratedValue(strategy=SEQUENCE)
private Long id;
#OneToMany(mappedBy = "doctor")
private Collection<DoctorDocument> documents = new ArrayList<>();
// any doctor-specific fields
}
#Entity
#Table(name = "XX_PATIENT")
public class Patient {
#Id
#GeneratedValue(strategy=SEQUENCE)
private Long id;
#OneToMany(mappedBy = "patient")
private Collection<PatientDocument> documents = new ArrayList<>();
// any patient-specific fields
}
// and so on
You can read a (doctor, patient, ...)'s documents from the relevant collection. You can even query BaseDocument instances based on any criteria.
You can even go ahead and do more fabcy stuff with the Java code. E.g. define an interface HasDocuments:
public interface HasDocuments<D extends BaseDocument> {
Collection<D> getDocuments();
}
Doctor, Patient, ..., implements this, so they can all be treated the same way.
I'm working on an apartment management software and I'm having an issue.
There is two entities I have :
#Entity
public class Tenant extends AbstractEntity { //AbstractEntity contains the id
#Column(nullable = false)
private int number;
#OneToOne
private Apartment apartment;
}
and
#Entity
public class Apartment extends AbstractEntity { //AbstractEntity contains the id
#Column(nullable = false)
private int number;
#OneToOne
private Tenant tenant;
}
But when I do
EntityManager em = emProvider.get();
em.getTransaction().begin();
em.merge(apartment);
em.flush();
em.getTransaction().commit();
It only save the Tenant into the Apartment but I would like it also update the Apartment into the Tenant.
Do I really need to set the apartment field into the tenant or there is a way to fix it simply?
Thanks
Cordially,
Baskwo
You need to declare CascadeType.ALL in your Apartment entity. See sample
#OneToOne(cascade = CascadeType.ALL)
private Tenant tenant;
CascadeType.ALL is for all CRUD operation. Adjust CascadeType depends on your application needs.
I have entity called Shop and different types of shops (sport, clothes, tech...). That list of types will probably be predefined. One shop can have multiple types. What is the best way to represent that?
I created two entities Shop and Type.
#Entity
#Table(name = "store")
public class Store {
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
private Long id;
...
}
#Entity
#Table(name = "type")
public class Type {
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
private Long id;
private String name; //sport, clothes, tech...
}
What type of relationship between these two entities should I use?
Given that you said Type is probably predefined, it seems more reasonable to model it as enum, and making use of ElementCollection
(I have to admit that I haven't tried to use them both in combination, I believe it should work though :P )
public enum Type {
SPORT, CLOTHES, TECH
}
public class Shop {
#Id
private Long id;
#ElementCollection
#CollectionTable(
name="SHOP_TYPE",
joinColumns=#JoinColumn(name="SHOP_ID")
)
#Column(name="TYPE")
// mapping for enum by usertype or other way, depending on JPA version you are using
private List<Type> types;
}
Of course, you can model SHOP_TYPE as an entity (e.g. ShopType) if you want more complicated operations on it, but what described above looks to me a more reasonable domain model.
Even you do not want the Type to be predefined (i.e. you can create whatever type in your application), it is still more reasonable to model it as a ManyToMany relationship:
public class Type {
#Id
#Column(name="TYPE_ID")
private Long id
#Column(name="TYPE_NAME")
private String name;
}
public class Shop {
#Id
#Column(name="SHOP_ID")
private Long id;
#ManyToMany
#JoinTable(
name="SHOP_TYPE",
joinColumns=#JoinColumn(name="SHOP_ID"),
inverseJoinColumns=#JoinColumn(name="TYPE_ID"))
private List<Type> types;
}
Just one thing to note: It does not look right to have a Type entity which contains a String as type name, and refer to Shop (as some of the answer suggested). Type should be an entity of itself, and, for example, different shops having CLOTHES type should refer to same CLOTHES entity (unless you view types as some kind of arbitrary tag)
The Store and Type many to many relationship is linked with a third / join table named STORE_TYPE_MAPS.
Store Entity:
#Entity
#Table(name = "store")
public class Store {
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
private Long id;
#ManyToMany(fetch = FetchType.LAZY, targetEntity=Type.class)
#JoinTable(name="STORE_TYPE_MAPS",
joinColumns=#JoinColumn(name="STORE_ID"),
inverseJoinColumns=#JoinColumn(name="TYPE_ID")
private Set<Type> types;
//... getter-setter
}
If Type is an Entity then make it ManyToMany
#ManyToMany
#JoinTable(name="Store_Type")
private List<Type> types;
also it can be an enum
I rather prefer to create a new entity called ShopType, the ManyToMany relationship will be created as explained below.
This new Entity allows you to have extra columns in the join table, "ShopType", (which can't be done with a simple #ManyToMany). For example, you can add this information: "the number of articles of each type in each shop".
The code is as follows:
public class Shop {
#Id
#Column(name="SHOP_ID")
private Long id;
#OneToMany(mappedBy = "shop", cascade = CascadeType.ALL)
private List<JoinAchatType> joinShopType = new ArrayList();
}
public class ShopType {
#ManyToOne
#JoinColumn(name = "SHOP_ID")
private Shop shop;
#ManyToOne
#JoinColumn(name = "TYPE_ID")
private Type type;
private int numberArticle;
}
public class Type {
#Id
#Column(name="TYPE_ID")
private Long id;
#OneToMany(mappedBy = "type", cascade = CascadeType.ALL)
private List<JoinAchatType> joinShopType = new ArrayList();
}
For more information check these links:
Mapping many-to-many association table with extra column(s)
.
The best way to use the #ManyToMany annotation with JPA and Hibernate.
I am working on JPA project and I need your help.
I have two classes, “Person” and “Leader” which inherits from Person.
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(unique = true)
private String personId;
}
And
#Entity
public class Leader extends Person implements Serializable {
private List < Person > listTeam;
public void addPersonInTeam(Person e) {
listTeam.add(e);
}
}
My question Is, do I need to have JPA annotations #OneToMany or something else before private List listTeam in class Leader?
Thank you very much
You need to specify a mapping between the two classes because for Hibernate the association is not relevant here, you have to use annotations in both sides and I guess you will need a OneToMany mapping here :
Here's the mapping that you are seraching for:
In Person class:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(unique = true)
private String personId;
#ManyToOne
#JoinColumn(name="leader_id")
private Leader leader;
//getter and setter
}
In Leader class:
#Entity
public class Leader extends Person implements Serializable {
#OneToMany(mappedBy = "leader")
private List <Person> listTeam;
//getter and setter
public void addPersonInTeam(Person e) {
listTeam.add(e);
}
}
For further information you can see these links:
Hibernate – One-to-Many example (Annotation).
Hibernate One To Many Annotation tutorial.
Note:
I don't see the use of the field personId in the Person class, there's no need to use two differents ids.
EDIT:
To answer your questions:
The #JoinColumn(name="leader_id") is not mandatory, but it's used to specify the foreign key name.
If the relation is ManyToMany the mappedBy property is used to specify the owner of the relationship, you can see this answer for more details.
I have the following two entities in the DB ( structure is fixed ) which I am trying to map using JPA Annotations and EBEAN is ORM.
I have the following beans:
class Item {
public Long id;
public String name;
public Consignee intermediate;
public Consignee ultimate;
}
class Consignee {
public Long id;
public String name;
public String address;
public Item item;
}
And their corresponding tables:
Item
----
id
name
Consignee
---------
id:
name
address
item_id
type: [1,2] / 1: intermediate, 2:ultimate
the main entity is ITEM although the relationship is mapped from the consignee side.
How can I mapped this using the Annotations so that the consignees ( ultimate, intermediate ) are loaded when I fetch the Item object from DB ?
Could you please point me to the right direction
The two tables your are trying to map to Ebean are called Entity Models and the relation between Entity Consignee to Item is a One to Many Relationship.
Such relation can be mapped with a #OneToMany annotation on the Consignee side, and with an #ManyToOne on the Item side.
Also the field type of Consignee can be mapped with an Enumeration persisted as integer, and the remaining fields can be mapped via #Column annotation.
A possible implementation of your requirements could be something like:
public enum CONSIGNEE_TYPE {
INTERMEDIATE,
ULTIMATE
}
#Entity
#Table(name = "Consignee")
public class Consignee extends Model {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
public String name;
public String address;
#Enumerated(EnumType.ORDINAL)
public CONSIGNEE_TYPE type;
#Column(name="item_id")
#OneToMany(cascade = CascadeType.ALL, mappedBy = "consignee", fetch = FetchType.EAGER)
public List<Item> item = new ArrayList<Item>();
//TODO: Generate Constructors/Getters/Setters
}
#Entity
#Table(name = "Item")
public class Item extends Model {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
public String name;
#ManyToOne(optional = false, fetch = FetchType.EAGER)
public Consignee consignee = new Consignee();
//TODO: Generate Constructors/Getters/Setters
}
I removed the ambiguation with the ultimate and intermediate consignee type from the class Item since you can store the type of the consignee on the consignee itself.
Hope this helps.
As a very nice resource for the future, I recommend you to read the Unit Tests available on the source code of the Ebean itself. It ain't pretty but it helped me a lot!