I have modified the design of CPRQ a bit to help my database pattern
I have an Employee table and a Department table. Both have common properties
#Column(name="tenantIDPKFK")
private Integer tenantIdpkfk;
#Column(name="status")
private Integer status;
So I created a base class ABaseEntity like below
public class ABaseEntity {
public ABaseEntity() {
}
public ABaseEntity(int tenantIdpkfk, int status) {
this.tenantIdpkfk = tenantIdpkfk ;
this.status = status ;
}
#Column(name="tenantIDPKFK")
private Integer tenantIdpkfk;
#Column(name="status")
private Integer status;
I have extended EmployeeEntity with ABaseEntity
#Entity
#Table(name = "employee")
public class EmployeeEntity extends ABaseEntity{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "first_name")
#NotEmpty(message = "Please provide a name")
#NotBlank
private String firstName;
My CommandHandler runs the following code
EmployeeEntity savedEmployeeEntity = this.employeeRepository.saveAndFlush(employee);
this.mediator.emit(new EmployeeCreatedEvent(savedEmployeeEntity.getId()));
Database saved the object, but only id, firstname. Does not save tenant and status columns.
I know I am missing something silly. Please help.
EDIT
Adding #MappedSuperclass to the ABaseEntity class fixed the issue.
#MappedSuperclass
public class ABaseEntity {...}
Database saved the object, but only id, firstname. Does not save
tenant and status columns.
By default JPA doesn't consider the parent class in the orm (object-relational mapping) of the current class.
You have to specify on the parent class #Inheritance with the strategy to use or use the default one.
For example :
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class ABaseEntity {...}
More info here.
Related
I have 3 entities -
Course
Module
Timeline
Course is an independent entity with following attributes:
Course - (id Integer Primary Key, course_name)
#Id
#Column(name = "id")
Integer courseId;
#Column(name = "course_name")
String course_name;
Next up is another entity Module,
Every row in module is related to one course, and hence there is a one to one relationship between Module and Course.
Module - (module_id, module_name, module_type, duration)
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "module_id")
Integer module_id;
#Column(name = "module_name")
String module_name;
#Column(name = "duration")
Integer duration;
#ManyToOne
#JoinColumn(name="timeline_id", nullable=false)
private Timeline timeline;
Now, next is a timeline entity, which is also related to course i.e every timeline id belongs to one course id, but one timeline id can belong to multiple module_ids, and hence below code:
#Id
#Column(name = "timeline_id")
Integer timelineId;
#OneToMany( mappedBy = "timeline" )
private List<Module> module;
#OneToOne( cascade = CascadeType.ALL)
private Course course;
Can you please tell me what is the error over here.
ModuleRepository:
#Repository
public interface ModuleRepository extends JpaRepository<Module, Integer>{
public List<Module> findAllByTimelineTimelineId(Integer timelineId);
}
IModuleService
public interface IModuleService {
public List<Module> findByTimelineId(Integer timelineId);
}
ModuleServiceImpl
public List<Module> findByTimelineId(Integer timelineId) {
// TODO Auto-generated method stub
return moduleRepo.findAllByTimelineTimelineId(timelineId);
}
Controller
#RequestMapping("/gettimeline/{timeline_id}")
public List<Module> findByTimelineId(#PathVariable Integer timeline_id){
return moduleService.findByTimelineId(timeline_id);
}
Now when I run this url in Postman: http://localhost:8083/gettimeline/1
I get an infinite loop, I am unable to decode the error, also is there any problem with OneToMany mapping, I am new to JPA:
[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[
Please help, thank you in advance :)
The infinite loop issue is caused by the one-to-many relation. There are several ways of fixing this, but I find view model classes like shown below as the cleanest approach.
Please note that the owning side of the one-to-many relation is not included in the code below, only the many-to-one. This can be done the other way around, but from your code, I guess this is what you want.
TimelineVM class
package no.mycompany.myapp.misc;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
public class TimelineVM {
private Integer timelineId;
public TimelineVM(Timeline timeline) {
this.timelineId = timeline.getTimelineId();
}
}
ModuleVM class
package no.mycompany.myapp.misc;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
public class ModuleVM {
private Integer module_id;
private String module_name;
private Integer duration;
private TimelineVM timeline;
public ModuleVM(Module module) {
this.module_id = module.getModule_id();
this.module_name = module.getModule_name();
this.duration = module.getDuration();
this.timeline = new TimelineVM(module.getTimeline());
}
}
Controller method
#RequestMapping("/gettimeline/{timeline_id}")
public List<ModuleVM> findByTimelineId(#PathVariable Integer timeline_id){
return moduleService.findByTimelineId(timeline_id).stream().map(ModuleVM::new).collect(Collectors.toList());
}
I have a problem with JPA inheritance. The database model is also specially built. It contains several tables with the same attributes (the tables were intentionally cut by country) and all these tables connect to another table (OneToOne).
Here is an example of the data model:
usa_user, germany_user, austria_user. All these tables have the same attributes (id, name, address). Now the address was also built up according to the countries e.g. usa_address, germany_address, austria_address.
Now I don't know or have the problem that I have been mapping them correctly for a long time. I have the following:
// All Lombok Getter, Setter Args,...
#MappedSuperclass
public abstract Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
private Long id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id", referencedColumnName = "id")
#JsonIgnore
private User user;
private String name;
private String addr_num;
...
}
// All Lombok Getter, Setter Args,...
#MappedSuperclass
public abstract User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
private Long id;
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
#JsonIgnore
private Address address;
private String name;
}
#Entity
#Table(name = "usa_user")
public class UsaUser extends User {}
#Entity
#Table(name = "austria_user")
public class AustriaUser extends User {}
#Entity
#Table(name = "germany_user")
public class GermanyUser extends User {}
#Entity
#Table(name = "usa_address")
public class UsaAddress extends Address {}
#Entity
#Table(name = "austria_address")
public class AustriaAddress extends Address {}
#Entity
#Table(name = "germany_address")
public class GermanyAddress extends Address {}
But unfortunately this does not work. Every time I start it JPA notices that it can't map the Entities Address - User (which is understandable because they are not entities but abstract classes). What would be the best way to solve this? I want to avoid that I have to list the attributes in all these entities because it would be redundant.
The goal is to find out how I can use a #MappedSuperclass in a #MappedSuperclass.
MappedSuperclass is not queryable and thus also not joinable. You need to map this as an abstract entity with the table per class inheritance strategy. Just switch to #Entity on the Address and User and add #Inheritance(TABLE_PER_CLASS).
I'm writing 3 tables in the following relation:
Club class:
#Setter
#Getter
#Entity
#Table(name = "Club")
public class Club {
#Id
#GeneratedValue
private Long id;
private String name;
private String type;
private String mainPage;
private String logo;
#OneToMany(mappedBy="clubProductKey.club", cascade = CascadeType.ALL)
#JsonIgnoreProperties(value = "clubProductKey.club", allowSetters=true)
private Set<ClubProduct> clubProducts;
...
Product class:
#Setter
#Getter
#Entity
#Table(name = "Product")
public class Product {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy="clubProductKey.product", cascade = CascadeType.ALL)
#JsonIgnoreProperties(value = "clubProductKey.product", allowSetters=true)
private Set<ClubProduct> clubProducts;
...
ClubProduct class:
#Setter
#Getter
#Entity
#Table(name = "ClubProduct")
public class ClubProduct {
#EmbeddedId
private ClubProductKey clubProductKey;
...
ClubProductKey class:
#Setter
#Getter
#Embeddable
public class ClubProductKey implements Serializable {
#ManyToOne(cascade = {CascadeType.MERGE,CascadeType.REFRESH })
#JoinColumn(name = "club_id", referencedColumnName = "id")
#JsonIgnoreProperties(value = "clubProducts", allowSetters=true)
private Club club;
#ManyToOne(cascade = {CascadeType.MERGE,CascadeType.REFRESH })
#JoinColumn(name = "product_id", referencedColumnName = "id")
#JsonIgnoreProperties(value = "clubProducts", allowSetters=true)
private Product product;
...
ClubProductRepository class:
public interface ClubProductRepository extends JpaRepository<ClubProduct, ClubProductKey> {
public List<ClubProduct> findByClubProductKeyClub(Club club);
public List<ClubProduct> findByClubProductKeyProduct(Product product);
}
I try to save clubProduct like this:
#Service
public class ClubProductServiceImp implements ClubProductService {
#Autowired
private ClubProductRepository clubProductRepository;
...
ClubProduct savedClubProduct = clubProductRepository.save(clubProduct);
return savedClubProduct;
}
However I find that the clubProduct is not saved in the clubProducts list in the club or product entity, the list is null. Must I add lines like club.getClubProducts.add(clubProduct) or is there any other way to make it added automatically?
Thank you.
The #OnetoMany mapping in your Club class uses the attribute mappedby which means that it represents the owning side of the relation responsible for handling the mapping. However, we still need to have both sides in sync as otherwise, we break the Domain Model relationship consistency, and the entity state transitions are not guaranteed to work unless both sides are properly synchronized.
The answer is yes, you have to manage the java relations yourself so that the clubProducts gets persisted. You are using an instance of the repository class club to persist the data so , you should add a setter method like :
public void addClubProduct(ClubProduct clubProduct) {
if (clubProduct!= null) {
if (clubProduct== null) {
clubProduct= new ArrayList<ClubProduct>();
}
clubProducts.add(clubProduct);
clubProduct.setClubProduct(this);
}
}
also a method to remove it from the list and use these method in your code to set the values to the list properly before initiating save . Read related article
enter image description here In this image first address for empId 1 and last two records are empid 2 (empid 2 haveing to address)
file:///home/user/Pictures/fk.png
#Entity
#Table(name = "Employee")
public class Employee {
#Id
#GeneratedValue
private Integer id;
private String name;
private Integer sal;
#OneToMany(cascade = CascadeType.ALL,mappedBy="employee")
private List<Address> addresses;
//getter setter
Child entity
#Entity(name="Address")
public class Address {
#Id
#GeneratedValue
private Integer aid;
private String city;
private String state;
#ManyToOne
#JoinColumn(name="id")
private Employee employee;
//getter setter
Repository
#Repository
#RepositoryRestResource(path="employee")
public interface EmployeeRepo extends JpaRepository<Employee,Integer> {
}
Input from RestClient
{
"name":"rdhe",
"sal":"20000",
"addresses":[{
"city":"hyd",
"state":"ts"
}]
}
if i use spring data jpa then code will be
// jpa Repository
public interface EmployeeRepo extends JpaRepository<Employee,Integer> {
}
// EmployeeServer class
#Service
public class EmployeeService {
#Autowired
EmployeeRepo employeeRepo;
public void saveEmployee(Employee employee){
employeeRepo.save(employee);
}
}
// controller
#RestController
public class EmployeeController {
#Autowired
EmployeeService employeeService;
#PostMapping(path="/save")
public void saveEmp(#RequestBody Employee employee){
employeeService.saveEmployee(employee);
}
}
if i'll use spring-data-rest at that time no need to create employeeService and controller class
I was getting the same problem until JsonManagedReference came to my rescue.
Try changing your entities to include them like this:
In the Employee Entity:
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy ="employee")
#JsonManagedReference
private List<Address> addresses;
In the Address Entity:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id", nullable = false, updatable = false, insertable =true)
#JsonBackReference
private Employee employee;
I was not able to find why it works this way, so please let me know if you come to know :)
It is probably due to the fact that your mentioning #JoinColumn(name="id"). The name attribute in #JoinColumn defines the name of the foreign key field in the child table. Since you are specifying foreign key column as id on hibernate, it could be the issue. Please update it to the same name(ie fk_empid) as specified in database, it should work...
I have a entity (declared with 2 way)(some not influencing code part are ommited for readability)
Entity version 1.
#Entity
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Formula("(SELECT COUNT(w.id) FROM stock s LEFT JOIN warehouse w ON s.id=w.stock_id WHERE s.article_id = id)")
private int variants;
public int getVariants() {
return variants;
}
}
Entity version 2.
#Entity
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Transient
private int variants;
#Access(AccessType.PROPERTY)
#Formula("(SELECT COUNT(w.id) FROM stock s LEFT JOIN warehouse w ON s.id=w.stock_id WHERE s.article_id = id)")
public int getVariants() {
return variants;
}
}
respective DTO and ArticleMapper - MapStruct
#JsonIgnoreProperties(ignoreUnknown = true)
public class ArticleDTOCommon {
private Long id;
private String name;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class ArticleDTO {
private Long id;
private String name;
private int variants;
}
#Mapper(componentModel = "spring", uses = {})
public interface ArticleMapper{
ArticleDTO toDto(Article article);
ArticleDTOCommon toDtoCommon(Article article);
}
I have a #Service layer on which how i know Hibernate creates it's proxy(for defining which field is fetch or not fetch) and transactions are occurs.
#Service
#Transactional
public class ArticleService {
#Transactional(readOnly = true)
public List<ArticleDTO> findAll() {
return articleRepository.findAll()
stream().map(articleMapper::toDto).collect(Collectors.toList());
}
#Transactional(readOnly = true)
public List<ArticleDTO> findAllCommon() {
return articleRepository.findAll()
stream().map(articleMapper::toDtoCommon).collect(Collectors.toList());
}
}
It works fine with fetching Related entity but
Problem is (fetching #Formula annotated field) when I am looking executed query on log it fetchs all time variants #Formula annotated query not depending on respective DTO.
But it must be ignored on toDtoCommon - i.e. It must not fetch variants field -> because when mapping Article to ArticleDtoCommon it not uses getVariant() field of Article. I have tried multiple ways as mentioned above.
I can solve it with writing native query(for findAllCommon() method) and map respectivelly with other way... But I want to know that how we can solve it with ORM way and where is problem.
Manupulating #Access type is not helping too.
Thanks is advance.