Hibernate attach and detach entity - spring

I have 2 entity classes
For ex,
#Entity
#Table(name = "AAA")
public Class AAA {
#ManyToOne(optional = false, cascade = {CascadeType.ALL})
#JoinColumn(name = "bbb_id", nullable = false)
#Valid
BBB bbb;
}
#Entity
#Table(name = "BBB")
public Class BBB {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "bbb_seq_generator")
#SequenceGenerator(sequenceName = "bbb_seq", name = "bbb_seq_generator", allocationSize = 1)
Long id;
}
I have a service which gets input request body as below json
"aaa" :
{
"bbb" : { "id" : 123}
}
I now use
aaaRepository.save(aaaObj);
to save both aaa and bbb
I want bbb to be saved only when its id value is not found in the DB (findById(id) gives Optional.empty) and retain the value if already found and just save aaa alone in that case.
Entity AAA has to be persisted and entity BBB also to be persisted if id is null in the request body and in case if id is already present then it has to be retained the same and AAA has to be persisted with whatever value comes in the request body
I tried but when i try to set bbb explicitly to aaa, I get detached entity exception. For the case when id is already present in request.
I can't be without setting the id as the service layer has to know which id it should check for in the DB.

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);

Spring Boot JPA - find by 2 columns

I don't know how to create a JPA query to get all records from my table:
#Entity
#Table(name = "A")
public class A{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, updatable = false)
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "message_id")
private Message;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
}
#Entity
#Table(name="message")
#Getter
#Setter
public class Message{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, updatable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "account_id", foreignKey = #ForeignKey(name = "FK_account_to_message"))
private Account account; //and then search by Account.id
}
SO I have 2 types of objects in A table (sometimes object is created from email, sometimes from a file):
one type without user_id (null) -> then I have to find all A object by searchning by Message -> Account -> Id
second type with user_id -> we can directly get A objects by values in user_id column
I want to get all records for specific user_id -> how to do that in most efficient way? I don't want to invoke 2 methods in repository:
User user = userService.getEmail();
List<A> aObjects= Stream.concat(ARepository.findByMessage_Account_Id(user.getId()).orElse(new ArrayList<>()).stream(),
aRepository.findByUser_Id(user.getId()).orElse(new ArrayList<>()).stream()).collect(Collectors.toList());
Is it possible to create ONE repository method that finds all records for 2 different objects (one with user_id and second without user_id)?
I guess that this query is to complex for using derived from the method name query. As it stated in the documentation:
Although getting a query derived from the method name is quite convenient, one might face the situation in which either the method name parser does not support the keyword one wants to use or the method name would get unnecessarily ugly. So you can either use JPA named queries through a naming convention or rather annotate your query method with #Query.
So, I would suggest just write the following query:
#Query("select aa from A aa left join aa.user u left join aa.message msg left join msg.account acc where (u is null and acc is not null and acc.id = :userId) or (u is not null and u.id = :userId)")
List<A> findByUserOrAccountId(#Param("userId") Long userId);

Zero to One (Optional One to One) Entity Relationship Issue (attempted to assign null one-to-one property)

Unable to update entity for optional one to one relationship using spring data jpa(2.1.2.RELEASE) and spring boot(2.1.2.RELEASE)
Getting the error attempted to assign null one-to-one property
#Entity
#Table(name = "table_a")
public class EntityA {
#Id
String id;
String aa;
int bbb;
#Nullable
#OneToOne(mappedBy = "inv", optional = true,cascade = CascadeType.ALL)
EntityB bEntity;
}
#Entity
public class EntityB{
#Id
String id;
String aaa;
String nnnn;
#OneToOne
#MapsId
#JoinColumn(name = "id")
EntityA aEntity;
}
DAO Code as below
Optional eA = entARepo.findById("1234");
EntityA entA= null;
if (eA.isPresent()) {
entA= eA.get();
}
EntityB eB = entA.getBEntity();
if (Objects.isNull(eB)) {
eB= new EntityB();
eB.setAAA("12121");
eB.setAEntity(entA);
entA.setBEntity(entB);
}
repository.save(entA);
}``
I resolved this by using a join table instead of a shared primary key approach. would still to know how to make the shared primary key approach work for optional one to one relationship

Spring JPA #JoinColumn(unique = true) not working

My entities are as follows
#Entity
#Table(
name = "t1",
uniqueConstraints = {
#UniqueConstraint(name = "u1", columnNames = {"u1"}),
#UniqueConstraint(name = "u2", columnNames = {"u2_id"}) //This does not enforce uniqueness
}
)
public class t1
{
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
private String u1; //Uniqueness is enforced here
#OneToOne
#JoinColumn(unique = true) //This does not enforce uniqueness
private U2 u2;
}
#Entity
#Table(
name = "u2",
uniqueConstraints = {
#UniqueConstraint(name = "p1", columnNames = {"p1"})
}
)
public class u2
{
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
private String p1; //Uniqueness is enforced here
}
Creating and updating the entities by
t1 t1_0 = new t1("t1_0");
t1 t1_1 = new t1("t1_1");
u2 u2_0 = new u2("u2_0");
repository.save(t1_0);
repository.save(t1_1);
repository.save(u2_0);
t1_0 = repository.findOne(t1_0.getId());
t1_1 = repository.findOne(t1_1.getId());
t1_0.setu2(u2_0);
t1_1.setu2(u2_0);
After the transaction finishes t1_0.u2 is tied to u2_0 and t1_1.u2 is tied to u2_0. I expected that it would throw uniqueness constraint violation exception.
Do not understand what is wrong. Other threads on SO suggest that #JoinColumn(unique = true) should do it.
EDIT
This is wrapped in one transaction
t1_0 = repository.findOne(t1_0.getId());
t1_1 = repository.findOne(t1_1.getId());
t1_0.setu2(u2_0);
t1_1.setu2(u2_0);
EDIT2
For some reason I can't access the database either. The console view loads but after adding password and user blank page is shown (some network queries fail).
But I can see from log that foreign keys are created and unique constraint aswell
Hibernate: alter table driver add constraint UK_ssh305wwvomjtn6opolug33nj unique (cardo_id)
Hibernate: alter table car add constraint FKosnia01vhqwmm888uxrg4o6f6 foreign key (manufacturerdo_id) references manufacturer
Hibernate: alter table driver add constraint FK3yb5ci9sr6ieo6n4wwwef3puv foreign key (cardo_id) references car
When adding
My transactions were invalid. As they do not see each others data, I mistakenly created one within another.
Columns names (if more than one) should be introduced with commas.
Your convention is wrong
Disclaimer: not checked on real database
#Table(
name = "t1",
uniqueConstraints = {
#UniqueConstraint(name = "u1", columnNames = {"u1"}),
#UniqueConstraint(name = "u2", columnNames = {"u2", "id"}) // comma
}
)

Update history table with Old and New values using Spring Boot, JPA

Technologies used: Spring Boot, Spring Data JPA.
Problem Summary: I have a use case to update order_tracking table when ever any status change happen in the purchase_order table's
User Entity:
#Entity
#Access(AccessType.FIELD)
#Table(name = "USER")
public class User extends BaseEntity {
private static final long serialVersionUID = 1L;
#Id
#Column(length = 28)
#GeneratedValue(generator = "USER")
#SequenceGenerator(name = "USER", sequenceName = "USER")
#Access(AccessType.PROPERTY)
private Long id;
#Column(unique = true)
private String email;
private String firstName;
private String lastName;
}
User Table:
id email_id first_name last_name
------------------------------------------
101 test#eg.com Test Example
Purchase Order Entity:
#Entity
#Access(AccessType.FIELD)
#Table(name = "PURCHASE_ORDER")
public class PurchaseOrder extends BaseEntity {
private static final long serialVersionUID = 1L;
#Id
#Column(length = 28)
#GeneratedValue(generator = "PURCHASE_ORDER")
#SequenceGenerator(name = "PURCHASE_ORDER", sequenceName = "PURCHASE_ORDER")
#Access(AccessType.PROPERTY)
private Long id;
private Long userId;
private String trackingNumber;
private String status;
}
Purchase Order Table:
id user_id tracking_number status
-------------------------------------------
101 101 1001 DELIVERED
For each activity, the status will be updated in the order_tracking table like this
Order Tracking Table:
id order_id old_status new_status date
-----------------------------------------------------------------
1 101 NULL CREATED 2017-07-14 10:08:10
2 101 CREATED REQUESTED 2017-07-14 22:08:10
3 101 REQUESTED IN_TRANSIT 2017-07-15 12:08:10
4 101 IN_TRANSIT DELIVERED 2017-07-15 22:18:10
When ever any status change in the purchase_order table, then order_tracking table need to be updated with old and new statuses.
To achieve this, i got suggestions here
But, my use case is to update order_tracking table when ever any status change in purchase_order table.
Can anyone help me the best approach to achieve this.
One invasive way is to obviously manage this as part of your bean's logic:
public void setStatus(OrderStatus status) {
if ( this.status != null && !this.status.equals( status ) ) {
this.history.add( new OrderStatusTransition( this, this.status, status ) );
}
this.status = status;
}
In this use case, you'd audit the OrderStatusTransition entity.
From an Envers perspective, this transition entity and even your order status table are completely unnecessary as you can accomplish representing the same data by using an audit reader query fetching the same information needed.
The benefit then is that avoids you having to build any type of hook or mechanism for tracking when the status changes and simply use the purchase order table and Envers to do that automatically for you.
You then simply build a business method that gets the necessary representation of those changes you need via a AuditReader query at runtime rather than trying to handle this at data manipulation and dirty tracking time.

Resources