QueryDsl Projections leave a field blank - java

#Entity
public class DocumentConsolidated {
#Id private UUID id;
#OneToOne(fetch = FetchType.EAGER, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "metadata_id")
private DocumentMetadata documentMetadata;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "documentConsolidated")
private DocumentConfiguration documentConfiguration;
}
#Entity
public class DocumentConfiguration {
#Id private UUID id;
#OneToOne(fetch = FetchType.LAZY)
#MapsId
private DocumentConsolidated documentConsolidated;
}
// Service code:
QDocumentConsolidated qDoc = QDocumentConsolidated.documentConsolidated;
(JPQLQueryFactory) queryFactory
.select(Projections.fields(qDoc, /*qDoc.id, */qDoc.documentConfiguration, qDoc.documentMetadata))
.from(qDoc)
.innerJoin(qDoc.documentConfiguration)
.fetch();
This is only going 2 ways:
with qDoc.id: id is present, documentConfiguration is null
without qDoc.id: id is null, documentConfiguration is present
Why?
What I already checked: Hibernate query is always bringing documentConfiguration fields when I run it in a Postgres client. Bot documentMetadata is present in both cases.

Problem is not solved, but I worked around it by removing Projections from the mix:
// Service code:
QDocumentConsolidated qDoc = QDocumentConsolidated.documentConsolidated;
QDocumentConfiguration qCfg = qDoc.documentConfiguration;
QDocumentMetadata qMeta = qDoc.documentMetadata;
return queryFactory
.select(qDoc, qCfg, qMeta) // <-- get rid of Projections
.from(qDoc)
.innerJoin(qCfg) // <-- manual join on Lazy entity (qMeta is auto-joined)
.fetch().stream()
.map(tuple -> { // <-- entity creation, similar with Projections.bean()
DocumentConsolidated documentConsolidated = Objects.requireNonNull(tuple.get(qDoc));
documentConsolidated.setDocumentMetadata(tuple.get(qMeta));
documentConsolidated.setDocumentConfiguration(tuple.get(qCfg));
return documentConsolidated;
})
.collect(Collectors.toList());

Related

How to save relationship of (detached) objects in java spring boot (jpa-hibernate)

I want to save a relationship of two or more persons to each other with specific elements. A person with its skill and on a specific address should be connected to each other. Normaly I creat a table to save the ID's of each element and create a row in the table (in normal MySQL with PHP).
How do I solve this problem in Java Spring Boot (JPA-Hibernate-MySQL)?
When I create (or better ask for) an "object" (detached) from the repository of each element and want to save it in a new "repository" (database) then I got an error.
PartnerConnectionServiceImplementation (.java)
#Service
public class PartnerConnectionServiceImpl implements PartnerConnectionService {
#Autowired
private PartnerConnectionRepository partnerConnectionRepository;
#Autowired
private DanceSkillServiceImpl danceSkillDatabaseService;
#Autowired
private AddressLocationServiceImpl addressLocationDatabaseService;
#Autowired
private UserProfileServiceImpl userProfileDatabaseService;
#Override
public Optional<PartnerConnection> connectPartnersWithDanceSkillAndAddressLocation(long userIdPartner1, long danceSkillIdPartner1, long addressLocationIdPartner1, long userIdPartner2, long danceSkillIdPartner2, long addressLocationIdPartner2) {
Optional<UserProfile> userProfile1 = this.userProfileDatabaseService.getUserById(userIdPartner1);
Optional<UserProfile> userProfile2 = this.userProfileDatabaseService.getUserById(userIdPartner2);
Optional<DanceSkill> danceSkill1 = this.danceSkillDatabaseService.getDanceSkillById(danceSkillIdPartner1);
Optional<DanceSkill> danceSkill2 = this.danceSkillDatabaseService.getDanceSkillById(danceSkillIdPartner2);
Optional<AddressLocation> addressLocation1 = this.addressLocationDatabaseService.getAddressLocationById(addressLocationIdPartner1);
Optional<AddressLocation> addressLocation2 = this.addressLocationDatabaseService.getAddressLocationById(addressLocationIdPartner2);
if (
(userProfile1.isPresent()) && (userProfile2.isPresent())
){
Optional<PartnerConnection> theConnection = getPartnerConnectionOfPartners(
userProfile1.get(),
userProfile2.get());
if (theConnection.isPresent()) {
return theConnection;
}
}
if (
(userProfile1.isPresent()) && (userProfile2.isPresent()) &&
(danceSkill1.isPresent()) && (danceSkill2.isPresent()) &&
(addressLocation1.isPresent()) && (addressLocation2.isPresent())
) {
PartnerConnection newPartnerConnection = new PartnerConnection(
null,
userProfile1.get(),
danceSkill1.get(),
addressLocation1.get(),
userProfile2.get(),
danceSkill2.get(),
addressLocation2.get()
);
this.partnerConnectionRepository.save(newPartnerConnection);
return Optional.of(newPartnerConnection);
}
return Optional.empty();
}
...
PartnerConnection (.java)
// indicates the connecitons between partners/ users
#NoArgsConstructor
#AllArgsConstructor
#Data
#Entity
#Table(name = "partner_connection")
public class PartnerConnection {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToOne(
fetch = FetchType.LAZY,
cascade = CascadeType.ALL
)
#JoinColumn(
name = "firstmessage_fk", // foreign key
nullable = true
)
private UserMessage firstMessage;
#ManyToOne(
fetch = FetchType.LAZY,
cascade = CascadeType.ALL
)
#JoinColumn(name = "onepartner_fk", // foreign key
nullable = false)
private UserProfile firstPartner;
#OneToOne(
fetch = FetchType.LAZY,
cascade = CascadeType.ALL
)
#JoinColumn(
name = "firstpartnerdanceskill_fk", // foreign key
nullable = false
)
private DanceSkill firstPartnerDanceSkill;
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#JoinColumn(name = "firstpartneraddresslocation_fk", // foreign key
nullable = false)
private AddressLocation firstPartnerAddressLocation;
#ManyToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#JoinColumn(name = "secondpartner_fk", // foreign key
nullable = false)
private UserProfile secondPartner;
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#JoinColumn(name = "secondpartnerdanceskill_fk", // foreign key
nullable = false)
private DanceSkill secondPartnerDanceSkill;
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#JoinColumn(name = "secondpartneraddresslocation_fk", // foreign key
nullable = false)
private AddressLocation secondPartnerAddressLocation;
public PartnerConnection(UserMessage firstMessage, UserProfile firstPartner, DanceSkill firstPartnerDanceSkill, AddressLocation firstPartnerAddressLocation, UserProfile secondPartner, DanceSkill secondPartnerDanceSkill, AddressLocation secondPartnerAddressLocation) {
this.firstMessage = firstMessage;
this.firstPartner = firstPartner;
this.firstPartnerDanceSkill = firstPartnerDanceSkill;
this.firstPartnerAddressLocation = firstPartnerAddressLocation;
this.secondPartner = secondPartner;
this.secondPartnerDanceSkill = secondPartnerDanceSkill;
this.secondPartnerAddressLocation = secondPartnerAddressLocation;
}
}
The error org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: ... nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: ... appears on this.partnerConnectionRepository.save(newPartnerConnection);
Do you have any easy to understand suggestions?
I think your method should contain #Transactional annotation. You have marked all relationships as LAZY, so if you want to get them You need a transaction to load them into managed state from detached one then be able to attach it to the object you want to save

Hibernate identity generator issue

I am having a particular issue when trying to save a collection of objects with hibernate. It seems that when I have more than one object of the same type, hibernate fails to generate the identifier, so I get a org.hibernate.NonUniqueObjectException .
Example:
App1 --> urls
{strApplicationId:1;URLTypeEntity{strCode:1,strDescription:Reply},strURL:www.address1.com},
{strApplicationId:1;URLTypeEntity{strCode:1,strDescription:Reply},strURL:www.address2.com},
{strApplicationId:1;URLTypeEntity{strCode:2,strDescription:Home},strURL:www.address3.com}
If I do not have two URLs with the same URLTypeEntity on the collection, the error is not triggered
#Entity
#Table(name = "tbl_urls")
public class URLEntity
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="intCode")
private Integer intCode;
private String strApplicationID;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "intType", referencedColumnName = "intCode")
private URLTypeEntity objURLType;
private String strURL;
}
#Entity
#Table(name = "tbl_applications")
public class ApplicationEntity
{
#OneToMany(cascade = CascadeType.ALL, mappedBy = "strApplicationID")
private List<URLEntity> colURLs;
}
ApplicationEntity must also have an id.
The solution was changing the CascadeType from ALL To Merge
#OneToMany(cascade = CascadeType.ALL, mappedBy = "strApplicationID")
Changed To
#OneToMany(cascade = CascadeType.MERGE, mappedBy = "strApplicationID")

Remove Order in hibernate

I have 4 entities:
Profile which has a relation with companyContract:
#OneToMany(fetch = FetchType.EAGER, mappedBy = "profile", cascade = { CascadeType.ALL })
#Fetch(value = FetchMode.SUBSELECT)
#Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
private List<CompanyContract> companyContracts;
CompanyContract which has a relation with timesheet:
#OneToMany(mappedBy = "companyContract", cascade = { CascadeType.ALL },orphanRemoval = true, fetch = FetchType.EAGER)
#Fetch(FetchMode.SUBSELECT)
#Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
private List<Timesheet> timesheets;
#ManyToOne
#JoinColumn(name = "IDPROFILE")
private Profile profile;
Timesheet which has a relation with invoice:
#OneToMany(mappedBy = "timesheet", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
#Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
private List<Invoice> invoices;
#ManyToOne
#JoinColumn(name = "IDCONTRACT")
private CompanyContract companyContract;
Invoice:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_TIMESHEET")
private Timesheet timesheet;
So As you can see here, I'm using org.hibernate.annotations.CascadeType.DELETE_ORPHAN so I can delete the children of a parent.
If I execute this:
Profile p = companyContract.getProfile();
p.getCompanyContracts().remove(companyContract);
companyContract.setProfile(null);
profileService.update(p);
---> The order of removal should be:
Remove invoices --> Timesheets --> CompanyContract, No ?
And instead I'm getting this error:
org.hibernate.exception.ConstraintViolationException: Column 'IDCONTRACT' cannot be null
And I've checked, this error happens after profileService.updateProfile(p);
The problem seems to be that the column IDCONTRACTin the table that holds Timesheets has a NOT NULL restriction. Remove it and try again.
If you're autogenerating the schema, try adding #Basic(optional = true) to Timesheet.companyContract:
#Basic(optional = true)
#ManyToOne
#JoinColumn(name = "IDCONTRACT")
private CompanyContract companyContract;
This is working completely fine. I see all associated child entities getting successfully deleted.
Check the below code.
Use orphanRemoval = true like below instead of deprecated way
org.hibernate.annotations.CascadeType.DELETE_ORPHAN , which you are using.
#OneToMany(mappedBy = "companyContract", cascade = { CascadeType.ALL },orphanRemoval = true, fetch = FetchType.EAGER)
Find code below
public void check() {
System.out.println("Start check() in DummyDAOImpl");
Session session = sessionFactory.openSession();
CompanyContract companyContract = session.get(CompanyContract.class, 2);
Profile p = companyContract.getProfile();
p.getCompanyContracts().remove(companyContract);
companyContract.setProfile(null);
session.update(p);
session.flush();
session.close();
System.out.println("Executed check() in DummyDAOImpl");
}

Hibernate ManyToMany wtih column child removal

I have Many To Many with additional column. Here realization(getters/setters generated by lombok, removed for clarity):
public class User extends BaseEntity {
#OneToMany(mappedBy = "user",
orphanRemoval = true,
fetch = FetchType.LAZY
cascade = CascadeType.ALL,)
private List<UserEvent> attendeeEvents = new ArrayList<>();
}
#Table(
name = "UC_USER_EVENT",
uniqueConstraints = {#UniqueConstraint(columnNames = {"user_id", "event_id"})}
)
public class UserEvent extends BaseEntity {
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "event_id")
private Event event;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_response_id")
private UserResponse userResponse;
}
public class Event extends BaseEntity {
#OneToMany(mappedBy = "event",
orphanRemoval = true,
fetch = FetchType.EAGER,
cascade = CascadeType.ALL)
private List<UserEvent> userEvents = new ArrayList<>();
}
I want this - when i delete Event, All "UserEvents" connected to it should be removed. And if I delete User, all "UserEvents" should be removed too.
I delete my event(eventRepository is Spring Jpa interface):
eventRepository.delete(event);
Then retrieving UserEvents from db:
List<UserEvent> userEvents = userEventsId.stream()
.map(id -> entityManager.find(UserEvent.class, id)).collect(Collectors.toList());
And there is collection with 2 items(this is count of UserEvents), but they all "null".
I can't understand what happening and how to do it right.
I need them deleted and when I check collection there should be 0, instead of 2.
The delete says marked for deletion, please try calling flush after deletion, and then find.
I guess find goes to the database, find the two rows, but when trying to instantiate them, find the entities marked for deletion and then you have this strange behaviour.
Recomendation: try to abstract more from the database and use less annotations. Learn the conventions of names for columns and tables (if you need to) and let JPA do its job.

Understanding Hibernate cascade behavior with multiple item references

I don't understand a certain Hibernate behavior regarding an object that should be persisted by a Cascade.ALL setting, but is then regarded as unsaved in another reference in the same transaction.
Example:
OrderProposal proposal = new OrderProposal();
ProposalLineItem proposalLine = new ProposalLineItem();
proposalLine.setProposal(proposal);
proposal.addLineItem(proposalLine); //to be saved by cascade.all via proposal
saveOrUpdate(proposal);
Order order = new Order();
OrderLineItem orderLine = new OrderLineItem(); //to be saved by cascade.all via order
orderLine.setProposalLine(proposalLine); //proposalLine is not mapped as cascaded from orderLine
proposalLine.setOrderLine(orderLine);
order.addLineItem(orderLine);
saveOrUpdate(order);
If this is run in a single transaction, Hibernate throws when processing the cascades of the order object:
org.hibernate.TransientPropertyValueException: object references an
unsaved transient instance - save the transient instance before flushing : orderLine.proposalLine
Do I have to save proposalLine explicitly for getting this to work?
EDITED
Here are the affected hibernate mappings:
#Entity
#Access(AccessType.FIELD)
#Proxy(lazy = false)
#OptimisticLocking(type = OptimisticLockType.VERSION)
public class ProposalLineItem {
#ManyToOne(optional = false)
#JoinColumn(name = "proposal_id")
private OrderProposal proposal;
#OneToOne(mappedBy = "proposalLine")
#NotFound(action = NotFoundAction.EXCEPTION)
private OrderLineItem orderLine;
}
#Entity
#OptimisticLocking(type = OptimisticLockType.VERSION)
#Proxy(lazy = false)
#Access(AccessType.FIELD)
public class OrderProposal {
#OneToMany(mappedBy = "proposal", orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#OrderColumn(name = "listIndex", nullable = false)
private List<ProposalLineItem> lineItems;
}
#Entity
#Proxy(lazy = false)
#OptimisticLocking(type = OptimisticLockType.VERSION)
#Access(AccessType.FIELD)
public class OrderLineItem {
#OneToOne(optional = false)
#NotFound(action = NotFoundAction.EXCEPTION)
#JoinColumn(name = "proposal_line_id", nullable = false)
private ProposalLineItem proposalLine;
}
#Entity
#OptimisticLocking(type = OptimisticLockType.VERSION)
#Proxy(lazy = false)
#Access(AccessType.FIELD)
public class Order {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#JoinColumn(name = "order_id")
#IndexColumn(nullable = false, name = "listIndex", base = 0)
private List<OrderLineItem> lineItems;
}
Cascade is a convenient feature to save the lines of code needed to
manage the state of the other side manually.
Let us suppose one scenario,where one object depend on the other one and you want to delete it how you will delete it? You have to fire the 1 after other query manually if cascading not there. So in hibernate cascading attribute is mandatory where you are define relationship between objects so by defining it child class object also going to effect if you make any change to Parent class object.
Now you have mentioned cascade = “all” you are telling cascading will be apply for insert/update/delete
Cascading can be apply save/update/delete etc.
Now if cascade = “none” then only Parent class object will effected and not the child class.

Categories