Spring Boot JPA associations vs normal SQL join - spring-boot

I have three tables:
company:
+--------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| company_id | int(11) | NO | PRI | NULL | |
| company_name | varchar(45) | YES | | NULL | |
+--------------+-------------+------+-----+---------+-------+
legal:
+-------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| company_id | int(11) | YES | | NULL | |
| legal_price | double | YES | | NULL | |
+-------------+---------+------+-----+---------+----------------+
price
+------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| company_id | int(11) | YES | | NULL | |
| price | int(11) | YES | | NULL | |
+------------+---------+------+-----+---------+----------------+
There are two one-to-many associations between company->legal and company->price. The company data in company is rarely changed. Data in two other tables are persisted daily. The corresponding entities are as follows:
#Entity
#Table(name = "company")
public class CompanyEntity {
#Id
#Column(name = "company_id", nullable = false)
private Integer companyID;
#Column(name = "company_name")
private String companyName;
#OneToMany(mappedBy = "company", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private Set<LegalEntity> legal;
#OneToMany(mappedBy = "company", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private Set<PriceEntity> price;
======
#Entity
#Table(name = "legal")
public class LegalEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "company_id", nullable = false)
private CompanyEntity company;
#Column(name = "legal_price")
private Double legalPrice;
======
#Entity
#Table(name = "price")
public class PriceEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "company_id", nullable = false)
private CompanyEntity company;
#Column(name = "price")
private Integer price;
For persisting new data in legal or price, I first get the corresponding field in company entity with findBycompanyID:
CompanyEntity cmp = crep.findBycompanyID(13);
LegalEntity leg = new LegalEntity(cmp, 523.5);
lrep.save(leg);
For querying data, I use CriteriaBuilder API:
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<LegalEntity> criteriaQuery = criteriaBuilder.createQuery(LegalEntity.class);
Root<LegalEntity> root = criteriaQuery.from(LegalEntity.class);
criteriaQuery = criteriaQuery.select(root);
TypedQuery<LegalEntity> typedQuery = em.createQuery(criteriaQuery);
List<LegalEntity> results = typedQuery.getResultList();
for (LegalEntity legalEntity : results) {
log.info(legalEntity.toString());
}
This procedure could be done with a simple native SQL query:
SELECT c.company_name, l.legal_price from legal l
JOIN company c
ON l.company_id = c. company_id;
As I investigate the Hibernate SQL commands, I see:
Hibernate:
select
legalentit0_.id as id1_1_,
legalentit0_.company_id as company_3_1_,
legalentit0_.legal_price as legal_pr2_1_
from
legal legalentit0_
Hibernate:
select
companyent0_.company_id as company_1_0_0_,
companyent0_.company_name as company_2_0_0_
from
company companyent0_
where
companyent0_.company_id=?
Hibernate:
select
companyent0_.company_id as company_1_0_0_,
companyent0_.company_name as company_2_0_0_
from
company companyent0_
where
companyent0_.company_id=?
Hibernate:
select
companyent0_.company_id as company_1_0_0_,
companyent0_.company_name as company_2_0_0_
from
company companyent0_
where
companyent0_.company_id=?
Hibernate:
select
companyent0_.company_id as company_1_0_0_,
companyent0_.company_name as company_2_0_0_
from
company companyent0_
where
companyent0_.company_id=?
My question is, which method is more efficient? I prefer using CriteriaBuilder API, since coding is quite easier and less error-prone, but I have to query the company field before each persist.

Related

Spring Data JPA - how to make intermediate query in spring data jpa

I have Promotion, PromotionDetail, Product entities like this:
#Entity
#Table(name = "promotions")
public class Promotion implements Serializable {
#Id
private String id;
#OneToMany(
fetch = FetchType.LAZY,
mappedBy = "promotion",
cascade = CascadeType.ALL
)
private Collection<PromotionDetail> promotionDetails;
}
====
#Entity
#Table(name="product")
public class Product implements Serializable {
#Id
private String id;
#OneToMany(
mappedBy = "product"
)
private Collection<PromotionDetail> promotionDetails;
}
====
#Entity
#Table(name = "promotion_details")
public class PromotionDetail implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#ManyToOne
#JoinColumn(
name = "promotion_id",
referencedColumnName = "id",
nullable = false
)
#JsonIgnoreProperties("promotionDetails")
private Promotion promotion;
#ManyToOne
#JoinColumn(
name = "product_id",
referencedColumnName = "id",
nullable = false
)
#JsonIgnoreProperties({"promotionDetails"})
private Product product;
}
from the above entities i want to find a solution at ProductRepository to get list of Products based on Promotion Id
i tried with line of code like this but it doesn't work:
Page<Product> findProductsByPromotionDetails_Promotion_Id(long id, Pageable pageable);
can someone please help me
it's pretty simple right
Examples of data are as follows:
Product:
id
--------------
P0882021035821
P0882021035822
P0882021035823
P1482022025430
Promotion:
id discount
-------
1 12
2 13
3 12
Promition_Detail
id product_id promiotion_id
1 P0882021035821 1
2 P0882021035823 1
3 P1482022025322 1
4 P1482022025430 5
5 P1482022025322 5
when i execute above query to get list product with promotion _id = 1 I have a product list that has product_id like:
P0882021035821
P0882021035823
P1482022025322
P1482022025322
the issue is i have double product have product id equals P1482022025322
and the result I want is a list like this:
P0882021035821
P0882021035823
P1482022025322
when I execute : findProductsByPromotionDetails_Promotion_Id(1,1);

Composite primary key vs multiple primary keys

Having this entities:
User.java:
#Entity
#NoArgsConstructor
#Getter
#Setter
public class User {
#Id
private int id;
private String username;
#OneToMany(mappedBy = "owner")
#MapKey(name = "friend_id")
private Map<User, Friendship> friends = new HashMap<>();
}
Friendship:
#Entity
#Data
//#IdClass(Friendship.class)
public class Friendship implements Serializable {
#Id
private int owner_id;
#Id
private int friend_id;
private String level;
#ManyToOne
#MapsId("owner_id")
private User owner;
#ManyToOne
#MapsId("friend_id")
private User friend;
}
I though I must have #IdClass or #EmbeddedId if I want to use two or more primary keys. But as shown above, I could ommit either, and just declare two primary keys (this is what I mean it "compiles"). So the question is, why to even bother using either of those annotations and just declare more keys?
generated table:
+-----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| owner_id | int | NO | PRI | NULL | |
| friend_id | int | NO | PRI | NULL | |
| level | varchar(255) | YES | | NULL | |
+-----------+--------------+------+-----+---------+-------+
As it's mentioned in the hibernate documentation:
The restriction that a composite identifier has to be represented by a "primary key class" (e.g. #EmbeddedId or #IdClass) is only JPA-specific.
Hibernate does allow composite identifiers to be defined without a "primary key class" via multiple #Id attributes.
Although the mapping is much simpler than using an #EmbeddedId or an #IdClass, there’s no separation between the entity instance and the actual identifier. To query this entity, an instance of the entity itself must be supplied to the persistence context.
#Entity
public class Friendship implements Serializable {
/*
It's better to use object wrapper classes instead of the corresponding
primitive types. Because, for example, uninitialized Integer is null,
but uninitialized int is 0 that can be a legal id.
*/
#Id
private Integer ownerId;
#Id
private Integer friendId;
public Friendship() {
}
public Friendship(Integer ownerId, Integer friendId) {
this.ownerId = ownerId;
this.friendId = friendId;
}
// ...
}
Friendship friendship = entityManager.find(Friendship.class, new Friendship(ownerId, friendId));

Spring Boot JPA One To Many and Many to One With Multiple Tables

I have 5 Tables, A User can have many module/role/plant. user_master is the main table with user details and user_roles is the sub table with module/role/plant details. My doubt is how to write the relationship in Model Class.
user_master
------------
user_id int(10) unsigned
first_name varchar(50)
last_name varchar(50)
mail_id varchar(80)
user_status tinyint(4)
is_deleted tinyint(4)
created_by int(10)
created_date date
modified_by int(10)
modified_date date
user_roles
-----------
user_role_id int(10) unsigned
user_id int(10) unsigned
module_master_id int(10) unsigned
role_master_id int(10) unsigned
plant_master_id int(10) unsigned
module_master:
module_master_id int(10) unsigned
module_code int(10)
module_name varchar(50)
active_flag tinyint(4)
role_master:
role_master_id int(10) unsigned
module_master_id int(10) unsigned
role_code int(10)
role_name varchar(50)
active_flag tinyint(4)
plant_master:
plant_master_id int(10) unsigned
plant_code int(10)
plant_name varchar(50)
active_flag tinyint(4)
The remaining module/role/plant have their own masters, master_id are primary key to master tables.
I just want to write user_master and user_roles model class. One-to-many & many-to-one mapping is required.
For one to many, you can have like below in your entity class:
#OneToMany in user entity
#OneToMany(mappedBy = "user")
private List<Role> roles;
#ManyToOne at roles side
#ManyToOne
#JoinColumn(name = "user")
private User user;
#ManyToMany
#ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "user_role",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id")
)
private List<Role> roles= new ArrayList<>();
and role side:
#ManyToMany(mappedBy = "role")
private List<User> users = new ArrayList<>();
here is an example to have many to many with emdeded:
#Entity
public class UserRoleMaster implements Serializable {
#EmbeddedId
private UserRoleMasterId id;
#ManyToOne
#JoinColumn(name = "user_master_id", referencedColumnName = "user_master_id", insertable = false, updatable = false)
private UserMaster userMaster;
#ManyToOne
#JoinColumn(name = "user_roles_id", referencedColumnName = "user_roles_id", insertable = false, updatable = false)
private UserRoles userRoles;
//getter //setter //constructures
#Embeddable
public static class UserRoleMasterId implements Serializable {
#Column(name = "user_master_id")
protected Long userMasterId;
#Column(name = "user_roles_id")
protected Long userRolesId;
//constrcture //getter //setters
}
}

Unable to persist child class records using spring data jpa

Problem
1)I created a class employee that extends Person.I can persist employee records and not person.
Solution I implemented
1)Created modal classes Employee and Person.
Modal Classes
Employee
#Entity
#Table(name = "employee",
indexes = {#Index(name = "username", columnList = "username", unique = true)})
public class BaseEmployee extends Person {
public static final String OBJECT_KEY = "EMPLOYEE";
#Id
#GeneratedValue
private Long id;
// Who created this baseEmployee
private String userId;
// #Indexed(unique = true)
private String username;
#Enumerated(EnumType.STRING)
private ROLE role;
#Enumerated(EnumType.STRING)
private STATUS status;
private String designation;
Person
public class Person extends AbstractEntity {
private String firstName;
private String lastName;
private String contact;
private String email;
#Enumerated(EnumType.STRING)
private GENDER gender;
private String imageId;
private String address;
private String locationId;
private DateTime dob;
private double[] location;
private String pinCode;
Employee Controller
#PostMapping
ApiResponse<BaseEmployee> post(#RequestBody CreateEntry<BaseEmployee> baseEmployeeCreateEntry) {
BaseEmployee baseEmployeeToCreate = baseEmployeeCreateEntry.getEntry();
baseEmployeeToCreate.setStatus(STATUS.ACTIVE);
baseEmployeeToCreate = employeeService.post(baseEmployeeToCreate);
if (baseEmployeeToCreate != null)
authenticationService.setPassword(baseEmployeeToCreate, baseEmployeeCreateEntry.getPassword());
return ApiResponse.success().message("Created Successfully!").object(baseEmployeeToCreate);
}
public class CreateEntry<T> {
private T entry;
private String password;
Service
#Override
public <E extends BaseEmployee> E post(E employee) {
employee = employeeRepository.save(employee);
System.out.println(employee);
LOG.info("Admin data" + employee);
LOG.info("PUT employee {} {} {}", employee.getId(), employee.getFirstName(), employee.getEmail());
return employee;
}
Repository
public interface EmployeeRepository extends JpaRepository<BaseEmployee, Long> {}
#Expected Output
The Employee class Columns and Person class Columns
#Output Shown
mysql> select * from employee;
id | created_at | created_by | last_modified_at | last_modified_by | version | designation | role | status | user_id | username |
1 | 1519624346694 | anonymousUser | 1519624346694 | anonymousUser | 0 | csdcs | ADMIN | ACTIVE | string | admin |
3 | 1519624856504 | anonymousUser | 1519624856504 | anonymousUser | 0 | csdcs | ADMIN | ACTIVE | string | admin1 |
4 | 1519626598478 | anonymousUser | 1519626598478 | anonymousUser | 0 | csdcs | ADMIN | ACTIVE | string | admin2 |
Can anyone guide me what I am doing wrong.Thankx in advance?
Can you try adding #MappedSuperclass to Person entity?

Embeddable with #ManyToOne and AccessType.Property on Id field

I am trying to define a #Embeddable class with a #ManyToOne relationship to another entity. The #OneToMany side has a #Id field annotated with #Access(AccessType.Property). Here is an example code to illustrate:
#Embeddable
class Embeddable {
#ManyToOne
private ClassA classA;
}
#Entity
class ClassA implements Serializable {
#Id
#GeneratedValue
#Access(AccessType.Property)
private Long id;
public Long getId() { return id; }
}
#Entity
class ClassB {
#ElementCollection
private List<Embeddable> embeddables;
}
The problem is that the created table definition for the class Embeddable seems to be wrong. The column for ClassA is of type bytea and is named class_a instead of bigint and class_a_id. Also if i try to persist one dataset the related entity of ClassA is saved serialized as hex..Every other #Entity works as expected. Is this a bug or an error on my part?
Edit:
Table for Embeddable (current state):
| class_b_id | class_a |
| type: int | type: typea |
|:-----------|------------:|
| 123 | A3B4738D.. |
Table for Embeddable (needed state):
| class_b_id | class_a_id |
| type: int | type: int |
|:-----------|------------:|
| 123 | 2 |

Resources