I need based on parameter retrieve or not some associations from an entity. In the bellow example I need to get the records list only if a parameter is passed through my api. Can you recommend a way of achieving this using hibernate/spring data? I'm looking for the most clean and spring data-like approach.
public class Customer {
private UUID id;
#OneToMany(mappedBy = "customer")
private List<Record> records = new ArrayList<>();
}
public class Record {
private UUID id;
#Column(name = "customer_id", length = 36, columnDefinition = "varchar(36)", nullable = false)
private UUID customerId;
#JoinColumn(name = "customer_id", insertable = false, updatable = false)
private Customer customer;
}
My Repository is empty:
public interface CustomerRepository extends JpaRepository<Customer, UUID> {
}
On my service I'm doing something like:
Customer customer = customerRepository.findById(customerId).orElseThrow(() -> new CustomerNotFoundException("customerId", customerId));
But what I would like to do is something like:
if (showRecords) {
Customer customer = customerRepository.findById(customerId).orElseThrow(() -> new CustomerNotFoundException("customerId", customerId));
} else {
Customer customer = customerRepository.findByIdWithoutAssociations(customerId).orElseThrow(() -> new CustomerNotFoundException("customerId", customerId));
}
How about using the base findById to return just the Customer object and have another method findWithRecordsById to return customer+records using #EntityGraph?
public interface CustomerRepository extends JpaRepository<Customer, UUID>{
#EntityGraph(attributePaths = {"records"})
Customer findWithRecordsById(UUID id);
...
}
Related
I have two entity booking and travelAgentBooking, booking could exist by itself while travelAgentBooing must have one booking of it.
TABookingEntity is below
#Entity
#ApplicationScoped
#Table(name = "TABooking")
#NamedQuery(name = "TABooking.findAll", query = "SELECT t FROM TABookingEntity t ORDER BY t.id ASC")
public class TABookingEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TABookingId_seq")
#SequenceGenerator(name = "TABookingId_seq", initialValue = 1, allocationSize = 1)
private Long id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "booking_id", nullable = false)
private BookingEntity flightbooking;
// belong to upstream booking so we just store id here
private Long taxibookingid;
private Long hotelbookingid;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public BookingEntity getFlightbooking() {
return flightbooking;
}
public void setFlightbooking(BookingEntity flightbooking) {
this.flightbooking = flightbooking;
if (flightbooking != null) {
flightbooking.setTravelAgentBooking(this);
}
}
public Long getTaxibookingId() {
return taxibookingid;
}
public void setTaxibookingId(Long taxibookingid) {
this.taxibookingid = taxibookingid;
}
public Long getHotelbookingId() {
return hotelbookingid;
}
public void setHotelbookingId(Long hotelbookingid) {
this.hotelbookingid = hotelbookingid;
}
BookingEntity is below
#Entity
#ApplicationScoped
#Table(name = "booking")
#NamedQueries({ #NamedQuery(name = "Booking.findAll", query = "SELECT b FROM BookingEntity b ORDER BY b.d ASC"),
#NamedQuery(name = "Booking.findByFlight", query = "SELECT b FROM BookingEntity b WHERE b.flight = :flight"),
#NamedQuery(name = "Booking.findByDate", query = "SELECT b FROM BookingEntity b WHERE b.d = :d") })
public class BookingEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "bookingId_seq")
#SequenceGenerator(name = "bookingId_seq", initialValue = 1, allocationSize = 1)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "customer_id", nullable = false)
private CustomerEntity customer;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "flight_id", nullable = false)
private FlightEntity flight;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "travelAgentBooking_id", nullable = true)
private TABookingEntity travelAgentBooking;
#NotNull
#Column(name = "date")
private Date d;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public CustomerEntity getCustomer() {
return customer;
}
public void setCustomer(CustomerEntity customer) {
this.customer = customer;
if(customer != null)
customer.addBooking(this);
}
public FlightEntity getFlight() {
return flight;
}
public void setFlight(FlightEntity flight) {
this.flight = flight;
}
public Date getDate() {
return new Date(d.getTime());
}
public void setDate(Date d) {
this.d = d;
}
public TABookingEntity getTravelAgentBooking() {
return travelAgentBooking;
}
public void setTravelAgentBooking(TABookingEntity travelAgentBooking) {
this.travelAgentBooking = travelAgentBooking;
}
here is the code I creating booking firstly, and then set it to tabooking.
then I'm trying to update the booking since when it is created, there is no travelAngentBooking for it to associate.
Booking booking = flightService.createBooking(tabooking.getFlightbooking());
tabooking.setFlightbooking(booking);
,,,,,,,,,,,
,,,,,,,,,,,
tabookingService.create(tabooking);
flightService.updateBooking(tabooking.getFlightbooking().getId(), tabooking.getFlightbooking());
After running it the table of travelAgentBooking is perfect.
But booking table column referred to travelAgentBooking is always null for any booking object.
UPDATE:
#PUT
#Path("/{id:[0-9]+}")
#Operation(description = "Update a Booking in the database")
#APIResponses(value = { #APIResponse(responseCode = "200", description = "Booking updated successfully"),
#APIResponse(responseCode = "400", description = "Invalid Booking supplied in request body"),
#APIResponse(responseCode = "404", description = "Booking with id not found"),
#APIResponse(responseCode = "409", description = "Booking details supplied in request body conflict with another existing Booking"),
#APIResponse(responseCode = "500", description = "An unexpected error occurred whilst processing the request") })
#Transactional
public Response updateBooking(
#Parameter(description = "Id of Booking to be updated", required = true) #Schema(minimum = "0") #PathParam("id") Integer id,
#Parameter(description = "JSON representation of Booking object to be updated in the database", required = true) Booking booking) {
Customer customer = customerService.findById(booking.getCustomer().getId())
.orElseThrow(() -> new RestServiceException("We can't found customer", Response.Status.BAD_REQUEST));
if (!customer.equals(booking.getCustomer()))
throw new RestServiceException("use custoemr's own API for it update", Response.Status.BAD_REQUEST);
Flight flight = flightService.findById(booking.getFlight().getId())
.orElseThrow(() -> new RestServiceException("We can't found flight", Response.Status.BAD_REQUEST));
if (!flight.equals(booking.getFlight()))
throw new RestServiceException("use custoemr's own API for it update", Response.Status.BAD_REQUEST);
try {
bookingService.validateBooking(booking);
} catch (ConstraintViolationException ce) {
// Handle bean validation issues
Map<String, String> responseObj = new HashMap<>();
for (ConstraintViolation<?> violation : ce.getConstraintViolations()) {
responseObj.put(violation.getPropertyPath().toString(), violation.getMessage());
}
throw new RestServiceException("Bad Request", responseObj, Response.Status.BAD_REQUEST, ce);
} catch (UniqueFlightWithDateException e) {
// we are updating an existence flight, so ignore this as expected
}
try {
bookingService.update(id);
} catch (ServiceException e) {
Map<String, String> responseObj = new HashMap<>();
responseObj.put("id", "please ensure the id is associated with this number");
throw new RestServiceException("Bad Request", responseObj, Response.Status.NOT_FOUND, e);
}
bookingService.update(id);
return Response.ok(booking).build();
}
BookingEntity update(BookingEntity booking) {
log.info("BookingRepository.update() - Updating " + booking.getId());
em.merge(booking);
return booking;
}
From the original posted code, the problem is that you have two very independent unidirectional relationships and only setting one of them. Since they are independent, the other remains null and can not be anything other than null until it is set.
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "booking_id", nullable = false)
private BookingEntity flightbooking;
The join column sets a foreign key in the "TABooking" table to point at the bookingEntity. It requires this relationship reference be set to populate that foreign key value. Same thing with:
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "travelAgentBooking_id", nullable = true)
private TABookingEntity travelAgentBooking
It creates its own travelAgentBooking_id foreign key column in the "booking" table that will remain null until you update a booking instance and set this reference. If you only set one side, the other will always remain null in the database.
But there are two problems with the model and your expectations. First, from the comments, you didn't intend this to be two separate relationships and instead expect it to be a single bidirectional relationship. For that, you need a single foreign key, and to pick a side that 'owns' it. The side that owns it controls it:
#OneToOne(mappedBy "flightbooking", fetch = FetchType.LAZY)
private TABookingEntity travelAgentBooking
Using mappedBy tells JPA that the other side owns the relationship. The foreign key column then is only set when you set the TABookingEntity.flightbooking reference and save/merge the TABookingEntity instance.
Second is you are using JSON and so Json serialization and assuming it abides by your object model and the JPA mappings. It does not. JPA annotations are for your persistence provider to tell it how to serialize/deserialize your model into the database but mean nothing for JSON serialization (or xml or any other REST formats). You need to tell your JSON tool how to handle your relationships, and that completely depends on how you are going to be expecting and sending the JSON. There are many tutorials and different strategies to deal with this (see this link for a good primer), but easiest is usually just to pick parts of the graph and exclude them with #JsonIgnore:
#OneToOne(mappedBy "flightbooking", fetch = FetchType.LAZY)
#JsonIgnore
private TABookingEntity travelAgentBooking
This will mean that any JSON you receive representing a booking will have a null travelAgentBooking. So if you need to see or set this relationship, your api would have to send/receive TABookingEntity which would still have the flightbooking reference serialized. I picked this way because flightbooking owns the relationship, so it matches JPA, but it doesn't need to. You can and should figure out what works for your client application and it may be different from the JPA mappings. I would expect that bookings always need to know the TABookingEntity and you'll want that sent to the client, so you might put the #JsonIgnore annotation on the other side. If you do, you'll just have to be sure that when you want to change or add TABookingEntity, that you fix the TABookingEntity.flightbooking reference appropriately so that you don't null out the foreign key.
I have many tables that belong to the same Project by ID. When I reload a Project with an existing ID, I need to clear all entities from the database.
Controller:
#CrossOrigin
#RequestMapping(value = "projects", method = RequestMethod.POST)
public ResponseEntity<?> uploadProject(MultipartFile file) {
JsonDataDto projectDto = converterService.convertToDto(file, JsonDataDto.class);
if(projectRepository.exists(projectDto.getId())) {
// Delete all project entities from DB
projectService.delete(projectDto.getId());
}
// Save project to DB
importService.import(projectDto);
}
Project Service (delete):
#Service
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)
public class GenericProjectService implements ProjectService {
// Fields
#Override
public void delete(UUID projectId) {
entity1Repository.deleteByProjectId(projectId)
...
// Most entities are associated with a project by a foreign key.
// Some entities are not linked by a foreign key and are removed manually (entity1Repository for example)
projectRepository.delete(projectId);
}
}
Import Service (save):
#Service
public class GenericImportService implements ImportService {
// Fields
#Override
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)
public void import(JsonDataDto projectDto) {
Collection<Entity1> entity1 = projectDto.getEntity1()
.stream().map(e -> e1Repository.save(e1Mapper.to(e))).collect(...);
Map<UUID, Type> types = new HashMap<>();
Map<UUID, TypeDto> typeDtosById = projectDto.getTypes().stream()
.collect(Collectors.toMap(TypeDto::getId, Function.identity()));
for (UUID typeId : typeDtosById.keySet()) {
saveType(typeId, typeDtosById, types, ...);
}
}
private void saveType(...) {
Type type = new Type();
// Set fields and relations
// Get exception here
type = typeRepository.save(type);
types.put(typeId, type);
}
}
Type Class:
#Entity
#Data
#Table(name = "...", schema = "...")
public class Type {
#Id
private TypePK typeId;
/*
#Data
#NoArgsConstructor
#AllArgsConstructor
#Embeddable
public class TypePK implements Serializable {
#Type(type = "pg-uuid")
#Column(name = "id")
private UUID id;
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "project_id", insertable = false, updatable = false)
private Project project;
}
*/
// Fields
#org.hibernate.annotations.Type(type = "pg-uuid")
#Column(name = "parent_id")
private UUID parentId;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "parent_id", referencedColumnName = "id", updatable = false, insertable = false),
#JoinColumn(name = "project_id", referencedColumnName = "project_id", updatable = false, insertable = false)})
private Type parent;
}
When the project does not exist in database, the save is successful. If I delete project from controller, it will also be successfully deleted from the database.
If project exists in database and I try to save it again, I get an error: "Unable to find package.Type with id TypePK(id=7e8281fe-77b8-475d-8ecd-c70522f5a403, project=Project(id=8d109d33-e15e-ca81-5f75-09e00a81a194))"
The entities are removed from the database, but the save transaction is rolled back.
I tried to force close the transaction after delete but it did not help:
public void delete(UUID projectId) {
TransactionStatus ts = TransactionAspectSupport.currentTransactionStatus();
entity1Repository.deleteByProjectId(projectId)
...
ts.flush();
}
The only way I found is, in fact, a crutch. I just wait a couple of seconds before starting save:
if(projectRepository.exists(projectDto.getId())) {
// Delete all project entities from DB
projectService.delete(projectDto.getId());
}
// Any timer
DateTime waitFor = DateTime.now().plusSeconds(2);
while(DateTime.now().isBefore(waitFor)) { }
// Save project to DB
importService.import(projectDto);
I managed to solve the problem using the suggestion in this comment: https://stackoverflow.com/a/14369708/10871976
I added an adapter to the delete transaction. On successful deletion in the "afterCompletion" method, I call project saving.
Note: for simplyfication i have changed some variables names and get rid of unnecessary code to show my issue.
I have two repositories:
#Repository
public interface CFolderRepository extends CrudRepository<CFolder, Long>, QuerydslPredicateExecutor<CFolder> {}
#Repository
public interface CRepository extends JpaRepository<C, Long>, CFinder, QuerydslPredicateExecutor<C> {}
The class C is:
#FilterDef(name = "INS_COMPANY_FILTER", parameters = {#ParamDef(name = "insCompanies", type = "string")})
#Filter(name = "INS_COMPANY_FILTER", condition = " INS_COMPANY in (:insCompanies) ")
#NoArgsConstructor
#AllArgsConstructor
#Audited
#AuditOverrides({#AuditOverride(forClass = EntityLog.class),
#AuditOverride(forClass = MultitenantEntityBase.class)})
#Entity
#Table(name = "INS_C")
#Getter
public class C extends MultitenantEntityBase {
#OneToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "C_FOLDER_ID")
private CFolder cFolder;
public void addFolder(List<String> clsUrl){
this.cFolder = CFolder.createFolder(clsUrl);
}
}
CFolder is:
#Getter
#NoArgsConstructor
#Audited
#AuditOverride(forClass = EntityLog.class)
#Entity
#Table(name = "C_FOLDER")
#AllArgsConstructor
public class CFolder extends EntityBase {
#Column(name = "CREATION_FOLDER_DATE_TIME", nullable = false)
private LocalDateTime creationFolderDateTime;
#Column(name = "UPDATED_FOLDER_DATE_TIME")
private LocalDateTime updatedFolderDateTime;
#Column(name = "FOLDER_CREATED_BY", nullable = false)
private String folderCreatedBy;
#Column(name = "FOLDER_UPDATED_BY")
private String folderUpdatedBy;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "cFolder", fetch = FetchType.EAGER)
#NotAudited
private Set<FolderDocument> folderDocuments = new HashSet<>();
public static CFolder createFolder(List<String> clsUrl){
CFolder cFolder = new CFolder(LocalDateTime.now(), null, SecurityHelper.getUsernameOfAuthenticatedUser(), null, new HashSet<>());
createFolderDocuments(clsUrl, cFolder);
return cFolder;
}
public void updateFolder(List<String> clsUrl){
this.updatedFolderDateTime = LocalDateTime.now();
this.folderUpdatedBy = SecurityHelper.getUsernameOfAuthenticatedUser();
this.folderDocuments.clear();
createFolderDocuments(clsUrl, this);
}
private static void createFolderDocuments(List<String> clsUrl, CFolder cFolder) {
int documentNumber = 0;
for (String url : clsUrl) {
documentNumber++;
cFolder.folderDocuments.add(new FolderDocument(cFolder, documentNumber, url));
}
}
}
FolderDocument is:
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Audited
#AuditOverride(forClass = EntityLog.class)
#Entity
#Table(name = "FOLDER_DOCUMENT")
public class FolderDocument extends EntityBase {
#ManyToOne
#JoinColumn(name = "C_FOLDER_ID", nullable = false)
private CFolder cFolder;
#Column(name = "DOCUMENT_NUMBER", nullable = false)
private int documentNumber;
#Column(name = "URL", nullable = false)
private String url;
}
And finally we have a service in which i use these entities and try to save/load them to/from database:
#Service
#AllArgsConstructor(onConstructor = #__(#Autowired))
public class CFolderService {
private final CRepository cRepository;
private final CommunicationClServiceClient communicationServiceClient;
private final CFolderRepository cFolderRepository;
public List<ClDocumentDto> getClCaseFolder(Long cId) {
C insCase = cRepository.findCById(cId);
List<ClDocumentDto> clDocumentsDto = getClDocuments(insCase.getCNumber()); // here, the object has one cFolder, but many FolderDocument inside of it
return clDocumentsDto;
}
#Transactional
public void updateCFolder(Long cId) {
C insC = cRepository.findCById(cId);
List<ClDocumentDto> clDocumentsDto = getClDocuments(insC.getCNumber());
List<String> clsUrl = clDocumentsDto.stream().filter(c -> "ACTIVE".equals(c.getCommunicationStatus())).map(ClDocumentDto::getUrl).collect(Collectors.toList());
if (Objects.isNull(insC.getCFolder())) {
insC.addFolder(clsUrl);
} else {
insC.getCFolder().updateFolder(clsUrl);
}
cFolderRepository.save(insC.getCFolder()); // here it saves additional FolderDocument instead of updateing it
cRepository.save(insC); // need second save, so can get these collection in getClaimCaseFolder successfully
}
}
I have two issues inside. In the example i was trying to clear the objects that i found from DataBase and create new ones.
1)
First is that i have to make two save operation to successfully restore the object in getClCaseFolder method (outside transactional).
2)
Second is that everytime i am saving - i get additional FolderDocument object pinned to CFolder object inside C object. I want to clear this collection and save new one.
I am not sure why hibernate does not update this object?
EDIT:
I think that i do sth like:
cRepository.save(insC);
instead of this.folderDocuments.clear();
i can do:
for(Iterator<FolderDocument> featureIterator = this.folderDocuments.iterator();
featureIterator.hasNext(); ) {
FolderDocument feature = featureIterator .next();
feature.setCFolder(null);
featureIterator.remove();
}
But i get eager fetching, why lazy wont work? There is an error using it.
Check whether you are setting ID in that Entity or not.
If ID is present/set in entity and that ID is also present in DB table then hibernate will update that record, But if ID is not present/set in Entity object the Hibernate always treat that object as a new record and add new record to the table instead of Updating.
Let's say we have the following three domain model entities: Company, Departament, and Employee.
#Getter #Setter #NoArgsConstrutor
public class Employee {
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "department_id", nullable = false, insertable = false, updatable = false)
private Department department;
#JoinColumn(name = "department_id", nullable = false)
private int department_id;
}
#Getter #Setter #NoArgsConstrutor
public class Department {
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "company_id", nullable = false, insertable = false, updatable = false)
private Company company;
#JoinColumn(name = "company_id", nullable = false)
private int company_id;
#OneToMany(mappedBy = "department")
private List<Employee> employees;
}
#Getter #Setter #NoArgsConstrutor
private class Company {
private String name;
#OneToMany(mappedBy = "company")
private List<Department> departments;
}
For each entity, we have Repositories which extend JpaRepository, Services, and Controllers. In each Service we #Autowire the respective Repository, and in each entity Controller we call methods from the entity Service.
My issue is the following: I cannot save an entire Company, because the Departments require a Company ID, and Employees a Deparment ID. So, firstly, in my CompanyService I save and then clear the departments list, do a saveAndFlush which assigns an ID to my company. I assign the received ID to every company_id in each entity of the previously saved departments list, then attach the list back to the company and do another saveAndFlush, and I do this one more time for the employee list.
#RestController
public class CompanyController {
#Autowire
private CompanyService companyService;
#PostMapping("/companies")
public Company createCompany(#RequestBody Company newCompany) {
return companyService.createCompany(newCompany);
}
}
#Service
public class CompanyService {
#Autowire
private CompanyRepository companyRepository;
public Company createCompany(Company company) {
List<Department> departments = new ArrayList<>(company.getDepartments());
company.getDepartments().clear();
companyRepository.saveAndFlush(company);
int company_id = company.getId();
departments.forEach (department ->
department.setCompany_id(company_id);
);
//here I save a copy of the previously saved departments, because I still need the employees
company.getDepartments().addAll(departments.stream().map(department -> department.clone(department)).collect(Collectors.toList()));
company.getDepartments().forEach(department -> department.getEmployees().clear());
companyRepository.saveAndFlush(company);
//here I assign each employee it's corresponding department ID
for (int i = 0; i < company.getDepartments().size(); i++) {
Department departmentInSavedCompany = company.getDepartments().get(i);
Department departmentWhichStillHasEmployees = departments.get(i);
departmentWhichStillHasEmployees.setId(departmentInSavedCompany.getId());
departmentWhichStillHasEmployees.getEmployees().forEach(employee -> employee.setDepartment_id(departmentInSavedCompany.getId()));
}
company.getDepartments.clear();
company.getDepartments.addAll(departments);
return companyRepository.saveAndFlush(company);
}
}
#Repository
public interface CompanyRepository extends JpaRepository<Company, Integer> {
}
I currenty do not like this implementation neither do I find it good. Which is the correct approach for this situation?
When working with JPA, do not work with IDs, work with object references.
In your case, this means removing the id attributes that duplicate the references.
In order to obtain the proper entities for IDs use JpaRepository.getOne. It will return either the entity if it is already in the 1st level cache or a proxy just wrapping the id, so it won't hit the database.
This allows you to assemble your object graph and persist it in one pass starting with the entity having no references to other entities.
You might also consider configuring cascading, if you consider entities to be part of the same Aggregate, i.e. they should be loaded and persisted together.
Hello I am trying to reference in a Criteria a property of a composite key which is defined as and #Embeddable on an Entity
#Entity
#Table(name = "B_J_P")
public class BJP implements java.io.Serializable {
private BJPId id;
private BJI bJI;
public BJP() {
}
public BJP(BJPId id, BJI bJI) {
this.id = id;
this.bJI = bJI;
}
#EmbeddedId
#AttributeOverrides( {
#AttributeOverride(name = "jIId", column = #Column(name = "J_I_ID", nullable = false)),
#AttributeOverride(name = "kN", column = #Column(name = "K_N", nullable = false, length = 100)),
public BJPId getId() {
return this.id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "J_I_ID", nullable = false, insertable = false, updatable = false)
public BJI getBJI() {
return this.bJI;
}
}
I need to reach the kName from the following:
#Embeddable
public class BJPId implements java.io.Serializable {
private long jIId;
private String kName;
public BJPId() {
}
public BJPId(long jIId, String kN) {
this.jIId = jIId;
this.kN = kN;
}
#Column(name = "J_I_ID", nullable = false)
public long getJIId() {
return this.jIId;
}
#Column(name = "K_NAME", nullable = false, length = 100)
public String getKName() {
return this.kName;
}
}
But when I am trying to reach it from the base class where BJP is a property with the following Criteria
DetachedCriteria timestampFilter = DetachedCriteria.forClass(BJP.class)
.createAlias("id","alias")
.add(Restrictions.eq("alias.kName","DataSetName"))
.setProjection(Projections.property("kName"));
I get the following error:
org.hibernate.QueryException: Criteria objects cannot be created directly on components. Create a criteria on owning entity and use a dotted property to access component property: id
at org.hibernate.loader.criteria.CriteriaQueryTranslator.getPathInfo
How should I formulate the criteria query in order to reach the kName property to apply filtering based on it in a dynamic sql context ?
If I have not provided enough relevant information, please ask what have I forgotten to provide full context.
EDIT: Upon Genzetto advice I have managed to reach the elements(at least it is not giving errors now) but returns no results once I do this:
DetachedCriteria timestampFilter = DetachedCriteria.forClass(BJP.class)
.add(Restrictions.eq("id.kName","DataSetName"))
.setProjection(Projections.property("id.kName"));
Session currentSession = sessionFactory.getCurrentSession();
Criteria query = currentSession.createCriteria(BJI.class)
.add(Subqueries.propertyEq("bJP",timestampFilter))
as upon looking at the SQL it is of the format
... where this_.J_INST_ID = (select this_.K_NAME as y0_ from .B_J_P this_ where this_.K_NAME=?)
it is trying to add the subquery to the ID of the root object although I want it part of bJP. How can I add it to proper location ?
You don't need to use an alias to do this. You can access directly to the composite key attributes:
DetachedCriteria timestampFilter = DetachedCriteria.forClass(BJP.class)
.add(Restrictions.eq("id.kName","DataSetName"))
.setProjection(Projections.property("id.kName"));