Unable to persist child class records using spring data jpa - spring-boot

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?

Related

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 JPA repository not flushing record to database

I see the response from repository.save() as updated object but same is not getting reflected in database.
I tried using JPArepository also and tried saveandflush method exposed by API but no luck.
Below are the details of code and configuration
Used sql driver with hibernate as middleware.
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=
spring.datasource.url=jdbc:mysql://localhost:3306/ccmBatch?useSSL=false
spring.batch.job.enabled=false
spring.datasource.initialize=false
spring.jpa.open-in-view=false
spring.jpa.hibernate.ddl-auto=create
spring.jpa.generate-ddl=true
spring.jpa.hibernate.use-new-id-generator-mappings=true
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.hibernate.naming.strategy=org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
POJO :
#Entity(name = "jobtracker")
public class JobTracker implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Column(name = "CCMBATCH")
String lockVal;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
long id;
public JobTracker() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getLockVal() {
return lockVal;
}
public void setLockVal(String lockVal) {
this.lockVal = lockVal;
}
}
Repository :
#Repository
public interface JobTrackerRepository extends CrudRepository<JobTracker, Long>{
}
Caller code :
#Autowired
JobTrackerRepository repsositoy;
#Transactional
public boolean acquireJobExecutionLock() {
LOGGER.info("acquire lock!!!");
String jobExecutionLockValue = new Date().toString();
JobTracker tracker = new JobTracker();
tracker.setLockVal(jobExecutionLockValue);
JobTracker job = repsositoy.save(tracker);
LOGGER.info("lock acquired!!!"+job.id+" "+job.lockVal);
return true;
}
job object used above is printing correct updated values.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.4.3</version>
</dependency>
mysql> desc jobtracker;
+----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| id | bigint | NO | PRI | NULL | |
| CCMBATCH | varchar(255) | YES | | NULL | |
+----------+--------------+------+-----+---------+-------+

Spring, JPA: How to query for Entities under another Entity with a many-to-many relationship bridge table setup

I'm fairly new to Spring. I'm trying to query all the donations under one donor with this ERD:
Donor |----* Agreement *----| Donations (A many-to-many relationship that uses a bridge table)
Here's my code:
Donor.java
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Donor extends Auditable implements Comparable<Donor>{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank(message = "Cannot have an empty account number field.")
private String accountNumber;
private String accountName;
private String salutation;
private String donorName;
private String cellphoneNumber;
private String emailAddress;
private String companyTIN;
private String phone1;
private String phone2;
private String faxNumber;
private String address1;
private String address2;
private String address3;
private String address4;
private String address5;
private String companyAddress;
private LocalDate birthDate;
private String notes;
#OneToMany(mappedBy = "donor")
List<MOA> moaList = new ArrayList<>();
...
}
Donation.java
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Donation extends Auditable implements Comparable<Donation> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank(message = "Cannot have an empty account number field.")
private String accountNumber;
private String accountName;
private String orNumber;
private String date;
private Double amount;
private String notes;
private String needCertificate;
private String purposeOfDonation;
#OneToMany(mappedBy = "donation")
List<MOA> moaList = new ArrayList<>();
...
}
MOA.java (Agreement)
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public class MOA extends Auditable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "donor_id")
#JsonIgnoreProperties("moaList")
private Donor donor;
#ManyToOne
#JoinColumn(name = "donation_id")
#JsonIgnoreProperties("moaList")
private Donation donation;
private String name;
private String donorAccountNumber;
private Long foreignDonationId;
private LocalDate dateSigned;
}
In my DonorRepository I'm trying to make this query which I expected would give me what I want:
public interface DonorRepository extends JpaRepository<Donor, Long> {
...
#Query(value = "SELECT * FROM donor WHERE account_number = ?1", nativeQuery = true)
List<Donation> findDonorsDonations(String accountNumber);
...
This gives me an error
Failed to convert from type [java.lang.Object[]] to type [com.package.server.domain.Donation] for value '{1, admin, 2021-04-01 10:29:53.0, admin, 2021-04-01 10:29:53.0, School, 123456, null, null, null, null, null, null, null, null, null, John Doe, null, null, null, null, null, Mr.}'; nested exception is org
You can use specification api and SpecificationExecutor.
You have to Join Donation with MAO(MAO with Donor) then query for donations of a particular Donor.

Spring Boot JPA associations vs normal SQL join

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.

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