I have two Entities Transaction and Category with ManyToOne mapping. So many transaction can fall into have category.
#Entity
class Transaction extends Model{
#Id
public Long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="cat_id", referencedColumnName="cat_id")
public Category cat;
}
#Entity
class Category extends Model{
#Id
#Column(name="cat_id")
public Long catId;
#Column(unique=true)
public String catName;
#ManyToOne
public List<Transaction> transactions
}
Now, when I add two transactions with same catName twice, it throws Unique constraint failure on catName. Is there any way I can instruct Ebean to merge Category, if CatName already exists (instead of always trying to insert)?
Also is this mapping approach correct, considering following:
If I delete Transaction, corresponding Category should not be deleted as it may be referenced by other Transactions.
Thanks for any help!
I think you have wrong annotation on Category model. If you want to list all Transaction data corresponded to any Category data. You should mark this with #OneToMany or #ManyToMany. As you marked your Transaction relation with Category as Many-To-One relationship, meaning that every Transaction has one Category associated to.
// This means every transaction has exactly one category associated
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="cat_id", referencedColumnName="cat_id")
public Category cat;
The relationship between Category with Transaction should be One-To-Many. The code below is a guidance how to fix your model:
#Entity
#Table(name = "category")
public class Category extends Model {
#Id
#Column(name="cat_id")
public Long catId;
#Column(unique=true)
public String catName;
// This means one category can have many transaction associated
#OneToMany(mappedBy = "cat")
public List<Transaction16507336> transactions;
}
It should allow you to save different Transaction object with same Category. Hope it is useful for you friend. :)
Update
The Category and Transaction model now have bi-directional relationship, this means if you have Category object you can also have Transaction object associated to, and vice versa. To save your model, you can follow this approach :
Category cat1 = Ebean.find(Category.class, 1L); // fetch category that exsist
Transaction t1 = new Transaction(); // this is new transaction
t1.cat = cat1; // cat1 category
t1.save();
Transaction t2 = new Transaction(); // this is new transaction
t2.cat = cat1; // cat1 category
t2.save();
Note: This reference may useful for you.
Related
I'm starting a project to know more in detail JPA.
Context:
At the end of his internship, the student has a report to make and a presentation in front of his professor to do about the internship.
I've a database, which is called "grade_management". It must contains a "student", "presentation", "report", "professor" and a "mark" (there are several rating criteria such as expression, quality of powerpoint ...) table. But now it's empty, since I want to make it throught JPA.
I've a "Presentation" class. Which countain this:
#Entity
public class Presentation implements Serializable {
#Id
#GeneratedValue (strategy=GenerationType.AUTO)
private int presentation_id;
private Date date;
private mark_id;
private int professor_id;
public Soutenance() {}
public Soutenance(Date date) {
this.date = date;
}
}
But the Presentation table contain 2 foreign key: professor_id and mark_id.
My question is: How can I indicate that both of them are foreign key ?
I'm sorry if I'm not clear, don't hesitation to ask question.
Cordially
You shouldn't reference other entities by their ID, but by a direct reference to the entity.
Something like that :
#ManyToOne
#JoinColumn(name = "mark_id", referencedColumnName = "id")
private Mark mark; // supposed here that mark_id if link to entity `Mark`
#ManyToOne
#JoinColumn(name = "professor_id", referencedColumnName = "id") // suppose "id" is the column name of the PK inside the table Professor.
private Professor professor; // supposed here that professor_id if link to entity `Professor`
This code is supposing that you use an unidirectional relation.
For bidirectional you have to define this in the other side (Mark/Professor type)
#OneToMany(mappedBy = "professor")
private Presentation presentation;
From your explanation, it looks like you have a Database named grade_management and in that database you have "student", "presentation", "report", "professor" and a "mark" tables (i.e: which are #Entity by themselves defined in their separate respective classes )
I'm not sure whether you have defined them or not. If not then you have to define them first and then use the refactored code mentioned below.
So, you will have many-to-one relation mapping. You can annotate your foreign keys belonging to different tables using #ManyToOne annotation to indicate relation type and #JoinColumn annotation to indicate that this entity has a foreign key to the referenced table.
You can redefine your Presentation class show below:
#Entity
#Table(name = "Presentation")
public class Presentation implements Serializable {
#Id
#Column(name="presentation_id")
#GeneratedValue (strategy=GenerationType.AUTO)
private int presentation_id;
private Date date;
#ManyToOne
#JoinColumn(name = "mark_id")
private Mark mark_id;
#ManyToOne
#JoinColumn(name = "professor_id")
private Professor professor_id;
public Soutenance() {}
public Soutenance(Date date) {
this.date = date;
}
//getter and setter
}
Also, if you need more information to read upon for yourself you can always checkout this Hibernate Documentation that explains everything you'll need to know.
I have a Project and Employee entities, which has ManyToMany relationship like below.
#Entity
public class Project {
#Id #GeneratedValue
private int projectId;
private String projectName;
// has some additional columns
#ManyToMany(mappedBy = "projects")
private List<Employee> emp = new ArrayList<Employee> ();
....
.....
}
#Entity
public class Employee {
#Id #GeneratedValue
private int id;
private String firstName;
private String lastName;
#ManyToMany(cascade=CascadeType.ALL)
List<Project> projects = new ArrayList<Project> ();
....
....
}
When I use above entities, JPA create a mpping table 'Employee_Project' like below.
create table Employee_Project (emp_id integer not null, projects_projectId integer not null)
My question is, whenever new employee is added, I want to update both employee table and Employee_Project mapping table only, assume I know project id that I would like to map this employee to. (without touching project table/entity, I mean why should I provide complete project object, while saving employee entity alone, how can I do this via jpa?)
You don't need to provide the entire Project object. Use EntityManager.getReference(projectId) or JpaRepository.getOne(projectId).
Those methods will create a proxy object with the appropriate id, rather than loading the entire Project entity from the data store.
EDIT Your service method should look pretty much like the following:
#Transactional
public void createEmployee(Employee employee, Long projectId) {
employee.setProjects(List.of(projectRepository.getOne(projectId));
employeeRepository.save(employee);
}
As a side note, CascadeType.ALL (in particular, because it includes CascadeType.MERGE and CascadeType.REMOVE) doesn't make sense for #ManyToMany. Unless you're planning to create a Project by creating an Employee, CascadeType.PERSIST makes no sense, either.
Let's assume the domain model of an application that is supposed to be built from scratch is described as follows:
A Person might live at an address. A Person can own multiple cars.
If I had to design the database first, I would probably come up with the following database design (normalization, cascading, etc. are not supposed to play a major role for my concrete question).
Person (id, name)
Address (id, street, zip, city, person_id)
Car (id, manufacturer, yearBuilt, color, person_id)
I have mainly followed standard design concepts (e.g. described in this link http://db.grussell.org/section006.html).
As you can see the address table has a foreign key to the person table as the person - address relationship can be considered optional.
The fact that a person can own multiple cars is implemented by putting a foreign key to a person in the car table. I think this is the standard way of modelling 1..m relationships.
If I had to design the domain model first, I would probably come up with the following design:
public class Person {
private String name;
private Address address;
private List<Car> cars;
// Getters and setters
}
public class Address {
private String street;
private String zip;
private String city;
// Getters and setters
}
public class Car {
private String color;
private Date yearBuilt;
// Getters and setters
}
In that domain model, the Person class has all necessary relationships. The Address and Car classes do not need to know anything about their owning Person.
I could now turn these classes into JPA entities by adding #Entity and providing an #Id attribute for every class.
#Entity
public class Person implements Serializable {
#Id
private Long id;
private String name;
#OneToOne
private Address address;
#OneToMany
private List<Car> cars;
public Person() { }
// Getters and setters
}
#Entity
class Address implements Serializable {
#Id
private Long id;
private String street;
private String zip;
private String city;
public Address() { }
// Getters and setters
}
#Entity
class Car implements Serializable {
#Id
private Long id;
private String color;
private Date yearBuilt;
public Car() { }
// Getters and setters
}
If my JPA provider creates the tables according to the provided annotations, the following database structure is created:
Person (id, name, address_id)
Address (id, street, zip, city)
Car (id, manufacturer, yearBuilt, color)
Person_Car (person_id, car_id)
As you can see, that does not correspond to the database structure I would create if I had to design the database first. I see a few flaws in the database model created by the JPA provider:
As the person - address relationship is optional, I would have put the foreign key to a Person into the Address table and not vice versa.
As the standard way of modelling a 1..m relationship is to put the foreign key of the owning class into the detail class, I would have never come up with a relation or association table. Why would I want to have that if the relation is not described by additional attributes?
To join a Person to a Car, the JPA provider needs to perform an additional join to the relation or association table. Does this measurably decrease the performance?
What I could do now is to provide the JPA entity classes with additional fields and/or annotations to strive for the database structure one might expect.
Is it desirable to strive for a domain model/JPA entity design that is able to create an expected database structure (as if database-first-approach was used)? If so, is it acceptable to have a domain model that is different from a domain model one would create intuitively? What are the advantages in designing a domain model/JPA entity model that will create some sort of "best practice" database structure?
Update the mapping from Person to Car as below:
#OneToMany
#JoinColumn(name = "person_id")
private List<Car> cars;
the DDL generator of your JPA provider should then create the desired table structure.
I have a couple of entities with a #ManyToOne and #OneToMany relationships, and the thing is that I am looking for a way to get the last record inserted from the #OneToMany side of the relationship without loading all the records from the list, Actually, I am saving this last record in a #OneToOne relationship in ClassB, but that creates too many conflicts with FK constraints. Here is an extract of how it looks like:
public class ClassA {
#Id
public Long id;
#ManyToOne
public ClassB classB;
#Temporal(javax.persistence.TemporalType.TIMESTAMP)
public Date startDate;
}
public class ClassB {
#Id
public Long id;
#OneToMany(mappedby = "classB", fetch = FetchType.LAZY)
public List<ClassA> classAList = new ArrayList<>();
#OneToOne
public ClassA lastClassA; // Generates a bidirectional FK, and unable the entity for deletion.
}
The interaction between these classes is that ClassA is accumulated over time and is saved linked to ClassB, at the time of ClassB do some work, it only needs the last ClassA instance created (which would be the current ClassA), the previous instances of ClassA are not important since they are in the past now, and are only kept for history. So, because of this I created the field lastClassA which is updated every time a new ClassA is created and saved on disk.
I could also create a JPQL to get this last Record from the classAList field (demo bellow), but I am not sure that is the best way to do it. So, does anybody know about a strategy to get this last record without loading the entire list and selecting the last member of the list?
Select a from ClassA a where a.startDate = (Select MAX(b.startDate) from ClassA b where ClassB.id=:classBId)
Say I have this class:
#Entity
#Table(name="PICTURE")
public class Picture{
private String category1, category2;
}
but the database structure looks like this:
TABLE PICTURE {
int category1;
int category2;
...
}
TABLE PICTURE_REF {
int category;
String categoryName;
...
}
How would I use JPA annotations on Picture so that any time I request an instance of it, category1 and category2 contains the categoryName from the PICTURE_REF table instead of the actual integer id stored in the PICTURE table?
I'm also wondering how saves would work because the user would select a category from a dropdown and the corresponding category integer ID would be what's stored in the PICTURE table.
From your description , PICTURE.category1 and PICTURE.category2 have the many-to-one relationship to the PICTURE_REF
The following shows the bi-directional mapping between them using annotation:
For table PICTURE:
#Entity
#Table(name="PICTURE")
public Class Picture{
#Id
private Integer id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "category1")
private PictureRef category1,
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "category2")
private PictureRef category2;
//getter and setters
}
For table PICTURE_REF:
#Entity
#Table(name="PICTURE_REF")
public Class PictureRef{
#Id
private Integer id;
#OneToMany(mappedBy = "category1")
List <Picture> listOfCat1Picture= new ArrayList<Picture>();
#OneToMany(mappedBy = "category2")
List <Picture> listOfCat2Picture= new ArrayList<Picture>();
//getter and setters
}
Important Points:
#Entity marks the java class as an hibernate entity. It is mapped to the name of the table specified in the #Table
Use #ManyToOne to define the many-to-one relationship
In the relational database , many-to-one relationship is expressed by using the following foreign key constraint:
"Many side table" has a FK column which only accepts the PK of the "one side table".
In your case , these FK columns are PICTURE.category1 and PICTURE.category2. The name of these FK columns can be explicitly defined by the name attribute of #JoinColumn.
FetchType.EAGER makes that PictureRef will be eagerly fetched whenever Picture is loaded or get
Depending on your requirement , you can do the unidirectional mapping by omitting #OneToMany in the PictureRef.It will also work .But given PictureRef , you cannot access its Picture
Given a Picture instance , you can get its categoryName and categoryId by
picture.getCategory1().getCategoryName()
picture.getCategory1().getId()
picture.getCategory2().getCategoryName()
picture.getCategory2().getId()
If you can't modify the schema
You can modify your mapping so Category is an entity instead of just a String. Then you would have a OneToOne or (more likely) a ManyToOne from Picture to Category for category1 and category2.
If you CAN modify the schema
You can use an ElementCollection on Picture to store a List instead of having category1 and category2. This would give you a schema something like
TABLE PICTURE {
long key;
}
TABLE PICTURE_CATEGORY {
long picture_key;
String category_name;
}
OR you can again map Category to an entity and use a ManyToMany from Picture to Category which would give you a schema like
TABLE PICTURE {
long key;
...
}
TABLE PICTURE_CATEGORY {
long picture_key;
long category_key;
}
TABLE CATEGORY {
long key;
String name;
}
As for saving, you will can use a converter in whatever your view technology is that will converter from key to Category, or you can load the Category from the key in your controller and set it in the Picture before you save. I doubt you'll want saving a Picture to cascade into a Category.