Using nested property access in a JPA repository for composite key entity implemented using #EmbeddedID approach returning null values - spring

My SQL view :-
CREATE OR REPLACE VIEW my_view
AS
SELECT
col1,
col2,
col3,
col4,
col5,
col6
FROM
my_table
My JPA entity :-
#Entity
#Table(name = "my_view")
#Getter
#Setter
public class MyEntity {
#EmbeddedId
private MyPk myPk;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "col1", insertable = false, updatable = false)
private Entity1 entity1 ;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col2", insertable = false, updatable = false)
private Entity2 entity2;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col3", insertable = false, updatable = false)
private Entity3 entity3;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col4", insertable = false, updatable = false)
private Entity4 entity4;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col5", insertable = false, updatable = false)
private Entity5 entity5;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col6", insertable = false, updatable = false)
private Entity6 entity6;
}
My Embedded Id class:-
#NoArgsConstructor
#EqualsAndHashCode
#AllArgsConstructor
#Embeddable
public class MyPk implements Serializable {
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "col1", insertable = false, updatable = false)
private Entity1 entity1 ;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col2", insertable = false, updatable = false)
private Entity2 entity2;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col3", insertable = false, updatable = false)
private Entity3 entity3;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col4", insertable = false, updatable = false)
private Entity4 entity4;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col5", insertable = false, updatable = false)
private Entity5 entity5;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col6", insertable = false, updatable = false)
private Entity6 entity6;
}
My JPA Repository :-
public interface MyEntityRepository extends JpaRepository<MyEntity, MyPk> {
List<MyEntity> findByMyPk_Entity1_Id(Long id);
}
Entity1 has an attribute id
I have tried #IdClass approach for composite key and many other solutions related to composite key but all are giving me some kind of error during runtime
This above approach is giving me no runtime error but giving null elements when invoking findByMyPk_Entity1_Id method but its giving correct count of elements in list
I cant use any kind of sequence from table or any other unique column approach dur to my underlying code. Also I tried using #mapIds approach but its also giving null elements.
For some reason JPA repository is not able to convert into entities but its able to give correct count of entities fetched
Also when using findAll method of repository in debugger its giving me proper entities list with no null elements but when searching through nested property i am not able to get entities
spring-data-jpa:1.9.4 version
hibernate-jpa-2.1-api:1.0.0
java 8
< Its a legacy project :") >

I don't know what you really have mapped in your table/view that you really need all 6 columns to uniquely identify a row. Assuming you do, and assuming they are all references to other tables that you want to have mapped in your entity, there are a number of approaches. The easiest, IMO more flexible approach might be this:
#Entity
#Table(name = "my_view")
public class MyEntity {
#EmbeddedId
private MyPk myPk;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "col1")
#MapsId("entity1")
private Entity1 entity1 ;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col2")
#MapsId("entity1")
private Entity2 entity2;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col3")
#MapsId("entity1")
private Entity3 entity3;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col4")
#MapsId("entity1")
private Entity4 entity4;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col5")
#MapsId("entity1")
private Entity5 entity5;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col6")
#MapsId("entity1")
private Entity6 entity6;
}
#Embeddable
public class MyPk implements Serializable {
private Integer entity1 ;//use the ID type from Entity1's primary key
private Integer entity2;
private Integer entity3;
private Integer entity4;
private Integer entity5;
private Integer entity6;
}
Mappings in the MyPk embeddable class will be basic mappings, and pick up the column name settings from the entity ManyToOne mappings. Should you try to ever write one out (you can't write to views, but JPA treats everything as a table), you only need to set the appropriate relationships and JPA will extract the fk values to populate your MyEntity.myKey.entityX values for you.
Having both the embedded and the ManyToOne references means you can have the fk values within your entity and accessible without having to fetch the related entity just for the ID value.

Related

Spring Data persisting Phantom Child with Null value - not null property references a null or transient value

I have the following Entities in my Project:
#Getter
#Setter
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = { "purchaseId" }))
public class Purchase {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long purchaseId;
#Column(unique = true, nullable = false, length = 15)
private String purchaseNo;
#Column(nullable = false, length = 15)
private String batchCode;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "supplier.supplierId", foreignKey = #ForeignKey(name = "FK_purchase_supplier"), nullable = false)
private Supplier supplier;
#Column(nullable = false)
private LocalDate purchaseDate;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "purchaseId", nullable = false)
private List<PurchaseItem> purchaseItems;
private Double totalAmount;
#ManyToOne
#JoinColumn(name = "userId", nullable = false, foreignKey = #ForeignKey(name = "FK_invoice_purchases"))
private User staff;
#Column(length = 100)
private String remarks;
#Column(nullable = false, updatable = false)
#CreationTimestamp
private LocalDateTime createdAt;
private boolean isDeleted = false;
}
#Getter
#Setter
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = {"purchaseItemId"}))
public class PurchaseItem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long purchaseItemId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "purchaseId", insertable = false, updatable = false, foreignKey = #ForeignKey(name="FK_purchase_item"))
private Purchase purchase;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "productId", foreignKey = #ForeignKey(name="FK_product_item"), nullable = false)
private Product product;
private Double itemAmount;
#Column(nullable = false)
private Double quantity;
private Double itemTotalAmount;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#PrimaryKeyJoinColumn(foreignKey = #ForeignKey(name = "FK_purchacase_item_batch"))
private PurchaseProductBatch productPurchaseBatch;
public void setPurchaseProductBatch() {
PurchaseProductBatch productPurchaseBatch = new PurchaseProductBatch();
productPurchaseBatch.setProduct(this.product);
productPurchaseBatch.setQuantity(this.quantity);
productPurchaseBatch.setPurchaseItem(this);
this.productPurchaseBatch = productPurchaseBatch;
}
}
#Getter
#Setter
#Entity
#Table()
public class PurchaseProductBatch{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long productBatchId;
#ManyToOne(cascade = CascadeType.DETACH)
#JoinColumn(name = "productId", foreignKey = #ForeignKey(name = "FK_product_purch"))
private Product product;
private Double quantity;
#OneToOne(fetch = FetchType.EAGER)
#MapsId
private PurchaseItem purchaseItem;
private boolean isDeleted = false;
#OneToMany(cascade = CascadeType.PERSIST)
#JoinColumn(name = "productBatchId", foreignKey = #ForeignKey(name = "FK_purchase_batch_qty"))
private Set<InvoicePurchaseBatchQuantity> invoicePurchaseBatchQuantities;
}
During Purchase Insert, everything works fine. However, if I update the Purchase record in the database and add new PurchaseItem entry, I encounter the issue below:
org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value : com.be.entity.PurchaseItem.product; nested
I have debugged my application and I see that there is a Product instance inside all of the PurchaseItem. When I commented out the PurchaseProductBatch inside PurchaseItem, everything works fine so I conclude that it is the causing the issue. However, I don't understand how and why JPA seems to create phantom PurchaseItem Records with no value.
Also, if I only update an existing PurchaseItem entry in Purchase, I don't encounter any issues.

Query result Infinite Recursion on ManyToMany relationship on hibernate

I have a entity mapping like this:
As you can see it is the bidirectional relationship and the team_users table has it own primary key and extra column - active.
Key code in team entity:
#OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
private Set<TeamUsers> team_users = new HashSet<TeamUsers>();
Key code in user entity:
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<TeamUsers> team_users = new HashSet<TeamUsers>();
Key code in team_user entity:
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "TEAM_ID")
private Team team;
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
I have a API which will return all team information, then I have service class like:
#Autowired
private TeamRepository teamRepo;
public List<Team> listAll() {
return (List<Team>) teamRepo.findAll();
}
Then the chrome log me that I have a "undefined" value, and when I test it by postman it shows me the infinite loop value:
I want to figure out what is the best approach to fetch the data?
I want to all information that tells me the team situation, including team... users...active status , almost everthing.
Any suggestions?
update
I tried to use #JsonIgnore on intermidate table :
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "TEAM_ID")
private Team team;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
but I won't get user information in that case and it avoid infinite loop:
What else I can do to get all information for teams?

Delete just one side of a manytomany relationship Hibernate

I have two tables that have a manytomany relationship:
first one is ad ( represents all the products)
#Entity
#Table(name = "ad")
public class Ad {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "admin_id")
private Admin admin;
#ManyToMany(mappedBy = "ads", fetch = FetchType.LAZY)
private List<Order> orders = new ArrayList<>();
Second one is order:
#Entity
#Table(name = "`order`")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne( cascade=CascadeType.
#JoinColumn(name = "buyer_id")
private Buyer buyer;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "order_ad", joinColumns = {
#JoinColumn(name = "order_id", referencedColumnName = "id", nullable = false, updatable = false) }, inverseJoinColumns = {
#JoinColumn(name = "ad_id", referencedColumnName = "id", nullable = false, updatable = false) })
private List<Ad> ads = new ArrayList<>();
when I delete order using its repository that is representing a cancellation so I don't want the ads to be deleted as well.
How can I do that?
PS: I can't find a replacement for the orphanRemoval of the onetomany relationship

JPA - Issue with OneToOne relationship when two foreign keys are primary key to entity

Two foreign keys act as primary key in entity for OneToOne, I'm getting error "Provided id of the wrong type for class ....."
When I tried to POST data, It's getting inserted correctly but GET is not working.
If I change OneToOne to OneToMany it is working for POST & GET both.
Request:
{
"items": [
{
"applicant": {
"guests": [
{
"seqNumber": 1,
"name": "name",
"gender": "gender"
}
]
}
}
]
}
Back Reference:
reservation.getItems().forEach(i -> {
i.setReservation(reservation);
i.getApplicant().setItem(i);
i.getApplicant().getGuests().forEach(g -> g.setApplicant(i.getApplicant()));
});
Reservation Entity:
#Entity
#Getter
#Setter
public class Reservation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID_RESERVATION", nullable = false, updatable = false)
private String reservationId;
#OneToMany(mappedBy = "reservation", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonManagedReference
private Set<Item> items = new HashSet<>();
}
Item Entity:
#Entity
#Getter
#Setter
#IdClass(Item.ItemKey.class)
public class Item {
#Id
#Column(name = "ID_ITEM_RESERVATION", nullable = false, updatable = false)
private long itemReservationId;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_RESERVATION", referencedColumnName = "ID_RESERVATION", nullable = false, updatable = false)
#JsonBackReference
private Reservation reservation;
#OneToOne(mappedBy = "item", cascade = CascadeType.ALL)
#JsonManagedReference
private Applicant applicant;
#Data
static class ItemKey implements Serializable {
private Reservation reservation;
private long itemReservationId;
}
}
Applicant Entity:
#Entity
#Getter
#Setter
#IdClass(Applicant.ApplicantKey.class)
public class Applicant {
#Id
#OneToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "ID_RESERVATION", referencedColumnName = "ID_RESERVATION", nullable = false, updatable = false),
#JoinColumn(name = "ID_ITEM_RESERVATION", referencedColumnName = "ID_ITEM_RESERVATION", nullable = false, updatable = false)
})
#JsonBackReference
private Item item;
#OneToMany(mappedBy = "applicant", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonManagedReference
private Set<Guest> guests = new HashSet<>();
#Data
static class ApplicantKey implements Serializable {
private Item item;
}
}
Guest Entity:
#Entity
#Getter
#Setter
#IdClass(Guest.GuestKey.class)
public class Guest {
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "ID_RESERVATION", referencedColumnName = "ID_RESERVATION", nullable = false, updatable = false),
#JoinColumn(name = "ID_ITEM_RESERVATION", referencedColumnName = "ID_ITEM_RESERVATION", nullable = false, updatable = false)
})
#JsonBackReference
private Applicant applicant;
#Id
#Column(name = "S_NUMBER", nullable = false, updatable = false)
private Short seqNumber;
#Column(name = "N_NAME")
private String name;
#Column(name = "CD_GENDER")
private String gender;
#Data
static class GuestKey implements Serializable {
private Applicant applicant;
private Short seqNumber;
}
}
Expected output must be same as Request but getting error " ... Provided id of the wrong type for class ..."
Here is the code.

Spring MVC - loading data from database

I have two entities which are in many to many relation and I can't load Set <Category> categories. These fields are filled in the database.
#Entity
#Table(name="Product")
public class Product {
#Id
#GeneratedValue
private int idProduct;
private String status;
private String name;
#ManyToMany(fetch = FetchType.EAGER, mappedBy= "products")
private Set <Category> categories;
}
#Entity
#Table(name="Category")
public class Category {
#Id
#GeneratedValue
private int idCategory;
private String name;
#ManyToMany(fetch = FetchType.EAGER)
private Set <Product> products;
}
This returns nothing int the view and the loop doesn't rotate even once.
<c:forEach items="${product.categories}" var="items">
<p>${items.name}</p>
</c:forEach>
I join the schema. Could someone write what to do to make it work, please?
enter image description here
This not works.
#Entity
#Table(name="Category")
public class Category {
#Id
#GeneratedValue
private int idCategory;
private String name;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "Product_Category", joinColumns = {
#JoinColumn(name = "Category_idCategory", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "Product_idProduct",
nullable = false, updatable = false) })
private Set <Product> product;
Hibernate infers the sql that it needs to create from the annotation on the objects. Your product entity is telling hibernate to get information about the SQL join from the Category entity. This is from the mappedBy clause in the #ManyToMany annotation.
When it goes to the Category entity, it doesn't find what it needs, and it just gives an empty set.
Most #ManyToMany annotations are done with a join table. Here is a sample join table annotation
#JoinTable(name = "product_to_category", joinColumns = {
#JoinColumn(name = "category_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "product_id",
nullable = false, updatable = false) })
Depending on your schema, you might need to adjust the above annotation to get it working. It will give you a good start.

Resources