Save entity with Spring data, persistence & db contraints - spring-data

In my project I have e.g. an Employee which has several Mobile which has additional attributes, e.g.
Jon Doe, +911, private
Jon Doe, 123456, company
Jon Doe, 8978, company,
Mara Smith, 7888, private
Mara Smith, 458, company
and therefore I create
#Entity
public class Employee {
#Id
private int id;
#OneToMany(mappedBy="emp")
private List<Mobile> lstMobiles;
...
}
and for the Mobile I have
#Entity
#IdClass(MobileKey.class)
public class Mobile {
#Id
private String phoneNo;
#Id
#JoinColumn(name = "emp_id")
private Employee emp;
...
}
class MobileKey{
private String phoneNo;
private int emp;
....
}
The database has tables Employee, Mobile. The table Mobile first two columns are
emp_id (as int)
phoneNo (as String)
up till now nothing special except that the database has a foreign constraint, namely that the emp_id has to exist in the table Employee. Otherwise the phoneNo does not make much sense.
The major problem I'm facing right now is that I need to store Jon Doe or Mara Smith. If I use e.g.
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
}
with some Autowired variable as e.g. employeeRepo.save(newEmployee) than I get from the database a constraint violation. This is due to the fact that the Mobile is attempted to be inserted before the Employee is created in the table :-(
I guess that the design of Employee and the Mobile is somehow wrong - especially with the list and the relation. But could be as well be related with the Spring-Data configuration.

Related

JPA: Update mapping table alone

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.

Is the domain model/JPA entity model supposed to create a well-designed database table structure?

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.

JPA #ManyToOne with different datatypes

I have two old database tables, that i need to use with JPA.
TOUR VEHICLE
----------------------- ---------------------
Id NUMBER(10) VehicleNumber CHAR(3)
VehicleNumber NUMBER(3) LicensePlate CHAR(10)
In my JPA entities I want tu use a #ManyToOne relationship from TOUR to VEHICLE.
Vehicle Entity:
public class Vehicle {
#Id
#Column(length=3)
private String VehicleNumber;
...
Tour Entity:
public class Tour {
#Id
#Column(precision=3)
private BigDecimal Id;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="VehicleNumber", referencedColumn="VehicleNumber")
private Vehicle vehicle;
...
But this of course doesn't work since the one vehicle number is translatet to String and the other is translatet to a BigDecimal(precision=3).
So what can i do to join them? Non-numeric values should be ignored.
Thanks for any advice.
VehicleNumber in TOUR table is a number while in the other one is CHAR.
First you need to match the types (better not to try to hack the standards).
then for #Id put the column name not #Column(precision=3). so it should be:
public class Tour {
#Id
#Column(name = "Id")
private BigDecimal id;
...
}
and do the same for the other table.
Oh by the way, I suggest you to use Long for the Primary Key not BigDecimal unless you have to.
But for entities, you are ok to work with Long

ebean unidirectional #OneToOne relation with unique constraint

I have a User class:
#Entity
public class User extends Model {
#Id
public Long id;
public String email;
public String name;
public String password;
}
and a driver class
#Entity
public class Driver extends Model {
#Id
public Long id;
#OneToOne (cascade = CascadeType.ALL)
#Column(unique = true)
public User user;
}
I want to make sure that the user_id is unique inside the Drivers table. But the code above does not enforce that. (I can create multiple drivers with the same user id).
Ideally, I do not want to add the #OneToOne relations in the User class because there are several different roles inside my app (e.g. driver, teacher, agent etc.) and I don't want to pollute user class with all those relations.
How can I achieve this?
I have tried this code on the model for me, and it worked. One thing to be noted, that you must use #OneToOne annotation to let the ORM knows that you have foreign key reference to other model.
The model look like following:
#Entity
// add unique constraint to user_id column
#Table(name = "driver",
uniqueConstraints = #UniqueConstraint(columnNames = "user_id")
)
public class Driver extends Model {
#Id
public Long id;
#OneToOne
#JoinColumn(name = "user_id")
public User user;
}
It will generate evolution script like this :
create table driver (
id bigint not null,
user_id bigint,
constraint uq_driver_1 unique (user_id), # unique database constraint
constraint pk_driver primary key (id)
);
So, with this method you can make sure that you will have unique user reference on driver table.
Additional Info
Because there is an additional constraint, that is not handled by framework but by the database applied on the model (such as the unique constraint), to validate the input or handling the occurred exception, you can surround Model.save() or form.get().save() expression (saving-the-model) with try-catch block to handle the PersistenceException.

EclipseLink: is it possible to add #OneToMany relationship to class on an #EmbeddedId property as foreign key?

I want to do a Jaas login module using JPA to store my AuthUser and AuthUserRole. I'll focus on the JPA side on this question.
Here is what I would do in the Database (not at all legitimate SQL code but hopefully comprehensive):
TABLE AuthUser( INT uid, VARCHAR password )
PRIMARY KEY (uid)
TABLE AuthUserRole( INT uid, INT role )
PRIMARY KEY (uid , role)
FOREIGN KEY (uid) REFERENCE AuthUser.uid
It makes sense to me, one role can only be assigned to a user once.
Here is what I attempted to do in Java, not showing username and email in AuthUser:
#Entity
public class AuthUser {
#Id
private int uid;
private String password;
#OneToMany
private Set<AuthUserRole> roles;
}
#Entity
public class AuthUserRole {
#Embeddedid
private AuthUserRolePK authUserRolePK;
}
#Embeddable
public class AuthUserRolePK {
public int uid;
public int role;
}
Eclipselink does the mapping just fine, which means it works, but not the way I wanted. It makes a third table named *authuser_authuserrole* that holds the (AuthUser_uid , uid, role) columns. No need to mention AuthUser_uid and uid is identical.
The trivial answer would be:
#Entity
public class AuthUser {
#Id
private int uid;
private String password;
#OneToMany
#JoinColumn(name="authUserRolePK.uid", referencedColumnName="uid")
private Set<AuthUserRole> roles;
}
But EclipseLink cryes that when #Embeddedid is used, all primary key columns have to be mentioned.
Q: How do I make Eclipselink map my entities like the database schema I mentioned? Should I rather use #IdClass? I could see the result of a database --> entities mapping but that's too easy :)
Thank you for your answers!
Three tables is the typical way to do this actually, your approach is simply lacking a little bit of JPA finesse.
Typically you have three tables associated as follows:
AuthUser AuthUser_Role (association) Role
frank ---- frank,admin ----- admin
This is in fact what Eclipselink was trying to map for you, but in general, you don't create the AuthUser_Role mapping yourself. Instead, you create a field on AuthUser like:
#ManyToMany
Set<Roles> userRoles
And (optionally) on Role like:
#ManyToMany(mappedBy="userRoles")
Set<AuthUser> usersWithRole;
Then EclipseLink takes care of the join table for you, and all you need to worry about is the userRoles field, which will update the join.
You can extend this to create the join manually for say roles that start and end on a set date, etc, but for all but the most complex projects, that's usually not necessary, and can often be accomplished in a different way. For compliance purposes, it's easier to use one of the ELUG extensions to keep and access a history of changes for example, which is the most common reason I've seen for adding extra meta-data to the join information.
Alternatively, if you REALLY want to do this with only two tables, make the AuthUserRole the primary side, and the AuthUser the passive side.
#Entity
public class AuthUser {
#Id
private int uid;
private String password;
#OneToMany(mappedBy="user")
private Set<AuthUserRole> roles;
}
#Entity
public class AuthUserRole {
#Embeddedid
private AuthUserRolePK authUserRolePK;
}
#Embeddable
public class AuthUserRolePK {
public AuthUser user;
public int role;
}
In this arrangement, you will end up with only two tables, and the "user" field will indeed be equal to the uid of AuthUser in the database, it just shows up as an object on the Java side.