Spring Boot bidirectional ManyToOne constraint - spring

Here are my 2 entities with a bidirectional ManyToOne relation. I deleted getters and setters to simplify the code.
#Entity
public class City {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
protected int id;
#Column(nullable = false, length = 50)
protected ZonedDateTime creationDate;
#Column(nullable = false, length = 50)
protected ZonedDateTime updatedDate;
#NotNull
#Size(min = 3, max = 20)
#Column(unique = true)
protected String name;
#OneToMany(mappedBy = "id")
#JsonBackReference
protected Collection<Spot> spots;
}
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = { "name", "address", "city_id" }))
#JsonIgnoreProperties(ignoreUnknown = true)
public class Spot {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
protected int id;
#Column(nullable = false, length = 50)
protected ZonedDateTime creationDate;
#Column(nullable = false, length = 50)
protected ZonedDateTime updatedDate;
#NotNull
#Size(min = 3, max = 30)
protected String name;
#NotNull
#NotBlank
protected String address;
#NotNull
#ManyToOne
#JoinColumn(name = "city_id")
// #JsonManagedReference
protected City city;
}
Because of the second constraint, i can't add more spots than the number of cities. IDs allowed for Spot is in the range of <1 to number of cities>.
Here is the error i get :
Cannot add or update a child row: a foreign key constraint fails (`myDatabase`.`spot`, CONSTRAINT `FKo3x3ohxcttkl2at3yr1xviw1r` FOREIGN KEY (`id`) REFERENCES `city` (`id`))
Any idea ? thank you

Related

How to write custom findAll() with Specification in JPA repository

When I use skuRepository.getAll(), it works OK, but when I apply filters, defined in Specification (List filteredRegs = skuRepository.getAll(specification)) I still get all the rows of the table
What should i do to apply the specifications to my custom method?
public interface SkuRepository extends CrudRepository<Sku, Integer>, JpaSpecificationExecutor<Sku> {
#Query("select s from Sku s join fetch s.unit un join fetch s.supplier sup WHERE un.id = sku_unit_id AND sup.id = supplier_id")
List<Sku> getAll(#Nullable Specification<Sku> var1);
#Query("select s from Sku s join fetch s.unit un join fetch s.supplier sup WHERE un.id = sku_unit_id AND sup.id = supplier_id")
List<Sku> getAll();
}
UPD:
Here is my entities.
When I make sampling by a Sku table using the Specification API, I see three separate selects in log: one for Sku entity, one for Unit and one for Suppliers. I want my app to make one select with joins.
I read that this is due to the fact that I use EAGER fetch type, so I change it to LAZY, but then I got another problem: "InvalidDefinitionException: No serializer found..." This is logical because related entities Unit and Supplier are not loaded.
Then I decided to write my custom getAll() with request:
#Query("select s from Sku s join fetch s.unit un join fetch s.supplier sup WHERE un.id = sku_unit_id AND sup.id = supplier_id ORDER BY s.name")
But now it does not support Specification.
Please advise what to do.
#Entity
#Table(name = "sku")
public class Sku implements Cloneable, CloneableEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "sku_code", length = 6, nullable = false, unique = true)
private String code;
#Column(name = "sku_name", nullable = false)
private String name;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinColumn(name = "sku_unit_id", nullable = false)
private Unit unit;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinColumn(name = "supplier_id", nullable = false)
private Supplier supplier;
#Column(name = "qty_in_sec_pkg")
private int quantityInSecondaryPackaging;
#Column(name = "sku_is_active", nullable = false)
private boolean isActive;
//constructors, getters, setters
}
#Entity
#Table(name = "units")
public class Unit {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id ")
private int id;
#Column(name = "unit", nullable = false, unique = true)
private String unit;
#Column(name = "description")
private String description;
//constructors, getters, setters
}
#Entity
#Table(name = "suppliers")
public class Supplier {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id ")
private int id;
#Column(name = "supplier_code", length = 6, nullable = false, unique = true)
private String supplierCode;
#Column(name = "supplier_name", nullable = false)
private String name;
#Column(name = "create_date", length = 19, nullable = false)
private String createDate;
#Column(name = "update_date", length = 19)
private String updateDate;
//constructors, getters, setters
}
You can't mix #Query and Specification
You can only use JpaSpecificationExecutor interface methods to use Specification.
Find more details here

springboot jpa combine two tables

I want to query data from two tables,
location field in Translation is a foreign key from id field of Location
#Entity
#Table(name = "Translation")
#Data
public class Translation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull private String language;
#NotNull private String name;
#NotNull private String description;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "location", nullable = false, insertable = false, updatable = false)
#Fetch(FetchMode.JOIN)
private Location location;
}
#Entity
#Table(name = "Location")
#Data
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull private String code;
#NotNull private String type;
private Double longitude;
private Double latitude;
#Column(name = "parent_id")
private Integer parentId;
#OneToMany(targetEntity = Translation.class, mappedBy="id", fetch = FetchType.EAGER)
private Set<Translation> translations;
}
————————————————————————————————————————
But when I use a query
#Query(
"SELECT new com.afkl.travel.exercise.model.RetrieveLocationResponse("
+ "loc.code, tran.name, loc.type, loc.latitude, loc.longitude, tran.description, loc.parentId)"
+ "FROM Location loc LEFT JOIN loc.translation tran")
List<RetrieveLocationResponse> fetchLeftJoin();
All the fields related to Translation is null, having no idea what happened
UPDATE
The following ones work for me.
#OneToMany(mappedBy = "location", cascade = CascadeType.ALL)
#JsonIgnore
private Set<Translation> translations;
#ManyToOne
#JoinColumn(name = "location")
private Location location;
try
#OneToMany(mappedBy = "location", cascade = CascadeType.ALL)
#JsonIgnore
private Set<Translation> translations;
#ManyToOne
#JoinColumn(name = "location")
private Location location;

Is there any way to delete entities from child entity using JPA?

I have 3 tables Table A, B, C. Table A is associated #OneToMany with Table B. Table B is associated #ManyToOne with Table C. Now when I find by Id of Table A, I am able to get details of A,B,C. But when I persist / delete, It is affecting only Table A&B. Table C is unaffected.
Is this possible in JPA to delete entities from child entity? Googled lot, but could not find any clue.
Below are the entity models of all the three tables
#Entity
#Table(name = "FEATUREMASTER")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class FeatureMaster implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#Column(name = "FGID")
private String featureid;
#Column(name = "FEATURENAME", nullable = false, unique = false)
private String featurename;
#Column(name = "DESCRIPTION", nullable = true, unique = false)
private String description;
#Column(name = "LIBNAME", nullable = true, unique = false)
private String libname;
#Column(name = "ISENABLED", nullable = false, unique = false)
private String isenabled;
#Column(name = "EDRULEGRP", nullable = true, unique = false)
private String edrulegrp;
// Do Not use - [orphanRemoval = true & CascadeType.ALL]- If used, deletion is not happening
#OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "FGID")
private List<CfgMaster> parameters;
// Getters and Setters
}
#Entity
#Table(name = "CFGMASTER")
public class CfgMaster implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#EmbeddedId
private CfgMasterPK id;
#Column(name = "CONFIGNAME", length = 45, nullable = true, unique = false)
private String parameter_name;
#Column(name = "CONFIGTYPE", length = 20, nullable = true, unique = false)
private String type;
#Column(name = "SUBPARAM", nullable = true, unique = false)
private Integer subparam;
#Column(name = "CONFIGDESCRIPTION", nullable = true, unique = false)
private String description;
#Column(name = "CONFIGLIMITFROM", nullable = true, unique = false)
private String from;
#Column(name = "CONFIGLIMITTO", nullable = true, unique = false)
private String to;
#ManyToOne(cascade = {CascadeType.ALL}, optional = true, fetch = FetchType.LAZY )
// #ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
#NotFound(action=NotFoundAction.IGNORE) // This is required to handle when no CfgData is found
#JoinColumns({
#JoinColumn(name = "FGID", insertable = false, updatable = false),
#JoinColumn(name = "DATAKEY", insertable = false, updatable = false)
})
private CfgData cfgData;
//Getters and Setters
}
#Entity
#Table(name = "CFGDATA")
public class CfgData implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
/*#EmbeddedId
private CfgDataPK id;*/
#Id
#Column(name = "FGID")
private String fgid;
#Id
#Column(name = "DATAKEY")
private String datakey;
#Column(name = "EPID", nullable = false, unique = false)
private int epid;
#Column(name = "RESERVED1", length = 45, nullable = true, unique = false)
private String reserved1;
#Column(name = "VALUE1", length = 100, nullable = true, unique = false)
private String value1;
#Column(name = "VALUE2", length = 100, nullable = true, unique = false)
private String value2;
//Getters and Setters
}
The problem I am facing is, I am not able to delete/save the entities of CfgData by passing FeatureMaster's primary id. Any operation I do is affecting only parent &child, not the grand child (CfgData) I tried a lot googling, but I cant find the solution.

SpringBoot: How to rollback creation of parent if child creation fails?

I have two models namely Company and User.
A Company can have many Users. I use Spring data jpa to interact with the database.
User Model :
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(length = 50, unique = true)
#NotNull
#Size(min = 4, max = 50)
private String username;
#NotNull
#Size(min = 8)
private String password;
#Column(length = 50)
#NotNull
#Size(min = 4, max = 50)
private String firstName;
#Column(length = 50)
#NotNull
#Size(min = 4, max = 50)
private String lastName;
#Column(length = 50, unique = true)
#NotNull
#Size(min = 4, max = 50)
private String email;
private Boolean enabled = true;
#Temporal(TemporalType.TIMESTAMP)
#UpdateTimestamp
private Date lastPasswordReset;
#Column(nullable = false, updatable = false)
#Temporal(TemporalType.TIMESTAMP)
#CreatedDate
private Date createdAt = new Date();
#Column(nullable = false)
#Temporal(TemporalType.TIMESTAMP)
#UpdateTimestamp
#LastModifiedDate
private Date updatedAt = new Date();
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(
name = "user_authority",
joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "authority_id", referencedColumnName = "id")})
private List<Authority> authorities;
#ManyToOne(optional = false, fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
#JsonBackReference
private Company company;
... Getter and Setters
}
Company Model
#Entity
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Size(min = 4, max = 100)
private String name;
#Size(max = 500)
private String description;
#OneToMany(mappedBy = "company", cascade = CascadeType.ALL)
#JsonManagedReference
private List<User> users = new ArrayList<>();
...Followed by Getter and Setters
}
So I use Spring Data JPA to save Company which takes care of creating a User as well.
So today if for some reason the creation of user fails due to some exception, the company record still exists.
So I need that if creation of User fails the Company shall not be created(rollback).
How can I achieve this.

Spring data JPA join table with extra column

I'm trying to implement a meeting model which contains multiple equipment entity with corresponding quantity.
In the view of meeting, user should be able to CRUD equipment and quantity of this equipment of a meeting
databases:
CREATE TABLE IF NOT EXISTS equipment (
equipment_id SERIAL PRIMARY KEY,
equipment_name VARCHAR(20) NOT NULL
);
CREATE TABLE IF NOT EXISTS meeting (
meeting_id SERIAL PRIMARY KEY,
meeting_time TIMESTAMP NOT NULL,
number_people INTEGER NOT NULL,
setup VARCHAR(255)
);
CREATE TABLE IF NOT EXISTS meeting_equipment (
meeting_equipment_id SERIAL PRIMARY KEY ,
meeting_id INTEGER NOT NULL REFERENCES meeting (meeting_id),
equipment_id INTEGER NOT NULL REFERENCES equipment (equipment_id),
quantity INTEGER NOT NULL DEFAULT 0
);
Entity implementation:
#Entity
#Table(name = "meeting")
#Data
public class Meeting {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "meeting_id", updatable = false)
#JsonIgnore
private int id;
#Column(name = "meeting_time")
#JsonFormat(pattern = "yyyy-MM-dd HH:mm")
#NotNull
private LocalDateTime meetingTime;
#Column(name = "number_people")
#NotNull
#Min(1)
private int numberPeople;
#Column(name = "setup")
#NotNull
private String setup;
#OneToMany(mappedBy = "meeting", cascade = CascadeType.ALL)
#JsonManagedReference
List<MeetingEquipment> equipmentList = new ArrayList<>();
}
#Entity
#Table(name = "equipment")
#Data
public class Equipment {
#Id
#Column(name = "equipment_id", updatable = false)
#JsonIgnore
private int id;
#NotNull
#Column(name = "equipment_name", unique = true)
#Size(min = 1, max = 100)
private String equipmentName;
}
Join table metting_equipment:
#Entity
#Table(name = "meeting_equipment", uniqueConstraints = {
#UniqueConstraint(columnNames = {"meeting_id", "equipment_id"})})
#Data
public class MeetingEquipment {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "meeting_equipment_id", updatable = false)
#JsonIgnore
private int id;
#ManyToOne
#JoinColumn(name = "meeting_id")
#NotNull
#JsonBackReference
private Meeting meeting;
#ManyToOne
#JoinColumn(name = "equipment_id")
#NotNull
private Equipment equipment;
#Column(name = "quantity")
#NotNull
private int quantity;
}
Using the code above, I can successfully create meeting with equipment included (JSON returned from creation method shows correct content). But once I try to remove an element of equipmentList in meeting entity, it does not delete meetingEquipment entity. I tried
meeting.getEquipmentList().clear() and meetingEquipmentDao.delete(meeting.getEquipmentList()), neither works.
Could anyone tell me the cause of this problem, thanks!

Resources