When I am implementing the Criteria API join for my spring boot study, I tried joining 2 classes and fetching the result. But when I am implementing and running I am getting the following error,
Unable to locate appropriate constructor on class [com.spacestudy.model.Investigator]. Expected arguments are: com.spacestudy.model.Employee
[cause=org.hibernate.PropertyNotFoundException: no appropriate constructor in class: com.spacestudy.model.Investigator]
And my Employee.java class like the following,
#Entity
#Table(name="employee")
public class Employee implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "employee_seq_generator")
#SequenceGenerator(name = "employee_seq_generator", sequenceName = "employee_seq",allocationSize=1)
#Column(name="nemp_id",columnDefinition="serial")
public Integer nEmpId;
#Column(name="semp_name")
public String sEmpName;
#Column(name="sdesignation")
public String sDesignation;
#Column(name="ninst_id")
public Integer nInstId;
#Column(name="ndept_id")
public Integer nDeptId;
#Column(name="sclient_emp_id")
public String sClientEmpId;
#Column(name="ntemp_emp_id")
public Integer nTempEmpId;
#Column(name="bis_paid")
public boolean bIsPaid=true;
#Column(name="sunpaid_comment")
public String sUnpaidComment;
#ManyToOne(optional=true)
#JoinColumn(name="ndept_id", insertable = false, updatable = false)
public Department department;
#OneToMany(cascade = CascadeType.ALL,mappedBy="nEmpId")
public Set<Investigator> employeeInvestigatorJoinMapping;
public Employee() {
}
public Employee(Integer nEmpId, String sEmpName, String sDesignation, Integer nInstId, Integer nDeptId,
String sClientEmpId, Integer nTempEmpId, boolean bIsPaid, String sUnpaidComment, Department department,
Set<Investigator> employeeInvestigatorJoinMapping) {
super();
this.nEmpId = nEmpId;
this.sEmpName = sEmpName;
this.sDesignation = sDesignation;
this.nInstId = nInstId;
this.nDeptId = nDeptId;
this.sClientEmpId = sClientEmpId;
this.nTempEmpId = nTempEmpId;
this.bIsPaid = bIsPaid;
this.sUnpaidComment = sUnpaidComment;
this.department = department;
this.employeeInvestigatorJoinMapping = employeeInvestigatorJoinMapping;
}
}
And my second class Investigator.java,
#Entity
#Table(name = "investigator")
#JsonInclude(JsonInclude.Include.NON_NULL) // avoiding null values
public class Investigator implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "investigator_seq_generator")
#SequenceGenerator(name = "investigator_seq_generator", sequenceName = "investigator_seq")
#Column(name="ninvestigator_id")
public Integer nInvestigatorId;
#Column(name="sinvestigator_name")
public String sInvestigatorName;
#Column(name="ninst_id")
public Integer nInstId;
#Column(name="stitle")
public String sTitle;
#Column(name="ntemp_investigator_id")
public Integer nTempInvestigatorId;
#ManyToOne(optional = false)
#JoinColumn(name="nemp_id",referencedColumnName="nemp_id")
public Employee nEmpId;
// Default Constructor.
public Investigator()
{
}
public Investigator(Integer nInvestigatorId, String sInvestigatorName, Integer nInstId, String sTitle,
Integer nTempInvestigatorId, Employee nEmpId) {
super();
this.nInvestigatorId = nInvestigatorId;
this.sInvestigatorName = sInvestigatorName;
this.nInstId = nInstId;
this.sTitle = sTitle;
this.nTempInvestigatorId = nTempInvestigatorId;
this.nEmpId = nEmpId;
}
}
And Implemented the Criteria API joining like the following,
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Investigator> cq = cb.createQuery(Investigator.class);
Root<Employee> rootInvestigator = cq.from(Employee.class);
Join<Employee ,Investigator> resultEmployeeMappingObj
= rootInvestigator.join("employeeInvestigatorJoinMapping");
cq.multiselect(rootInvestigator);
cq.where(cb.equal(resultEmployeeMappingObj.get("nEmpId"), 21638));
List<Investigator> results = em.createQuery(cq).getResultList();
return results;
Where did I go wrong?
Criteria API
You have a few mistakes in the Criteria API query.
The working one looks like this
#Transactional(readOnly = true)
public List<Investigator> findByEmployeeId(int employeeId) {
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Investigator> query = criteriaBuilder.createQuery(Investigator.class);
Root<Investigator> investigator = query.from(Investigator.class);
Join<Investigator, Employee> employees = investigator.join("nEmpId");
query.select(investigator)
.where(criteriaBuilder.equal(employees.get("nEmpId"), employeeId));
TypedQuery<Investigator> typedQuery = em.createQuery(query);
List<Investigator> investigators = typedQuery.getResultList();
log.debug("Investigators: {}", investigators);
return investigators;
}
Spring Data JPA
Also, if your application is based on Spring Framework after renaming a few fields you can use Spring Data JPA and do not write query at all.
Employee entity:
#Entity
#Table(name = "employee")
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "employee_seq_generator")
#SequenceGenerator(name = "employee_seq_generator", sequenceName = "employee_seq", allocationSize = 1)
#Column(name = "nemp_id", columnDefinition = "serial")
public Integer id;
//...
#OneToMany(cascade = CascadeType.ALL, mappedBy = "employee")
public Set<Investigator> investigators = new HashSet<>();
//...
}
Investigator entity:
#Entity
#Table(name = "investigator")
#JsonInclude(JsonInclude.Include.NON_NULL) // avoiding null values
public class Investigator implements Serializable {
//...
#ManyToOne(optional = false)
#JoinColumn(name = "nemp_id", referencedColumnName = "nemp_id")
public Employee employee;
//...
}
Spring Data JPA repository interface:
public interface InvestigatorRepository extends JpaRepository<Investigator, Integer> {
List<Investigator> findByEmployeeId(int employeeId);
}
That's it. Now you can simply inject the repository and use it:
#Autowired
private InvestigatorRepository investigatorRepository;
public void testQuery() {
investigatorRepository.findByEmployeeId(employee.getId()));
}
The exception seems to me that Criteria likes to have an Investigator constructor that takes an Employee argument:
public Investigator(Employee nEmpId) {
super();
this.nEmpId = nEmpId;
}
Related
maybe duplicate question but I couldn't fina a solution for my case which I think is pretty simple.
I have two tables like so :
And those are the related DTO Object :
First table
#Entity
#Table(name = "DA10003_REF_SIGNALEMENT")
public class RefSignalement {
#Id
#Column(name = "CD_SIGNALEMENT")
public String codeSignalement;
#Column(name = "LIBELLE")
public String libelle;
#Column(name = "CATEGORIE")
public String categorie;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "CD_SIGNALEMENT")
public List<RefMessage> refMessages;
}
Second table :
#Entity
#Table(name = "DA10004_REF_MESSAGE")
public class RefMessage {
#Id
#Column(name = "CD_SIGNALEMENT")
public String codeSignalement;
#Id
#Column(name = "DESTINATAIRE")
public String destinataires;
#Column(name = "MESSAGE")
public String message;
}
And the following query to get all the RefSignelement with the associated message :
List<RefSignalement> listRefSignalement = em.createQuery("SELECT p FROM RefSignalement p, RefMessage m", RefSignalement.class).getResultList();
Unfortunately it's returning an empty list, I have tried to change it with join fetch but nothing change.
Thank for the help
Remember that in JPQL you have to think in Objects, not relations. You want to fetch all 'RefSignalement' and eagerly fetch their 'refMessages' properties:
SELECT DISTINCT s FROM RefSignalement s JOIN FETCH s.refMessages
Here the "distinct" is only needed by JPA when assembling your resulting entities, but add unnecessary overhead to the SQL Query. If you have a Hibernate version >= 5.2.2 (I think), then there is a query hint you can use to avoid that:
List<RefSignalement> sigs = entityManager
.createQuery(
"select distinct s " +
"from RefSignalement s " +
"left join fetch s.refMessages ")
.setHint("hibernate.query.passDistinctThrough", false)
.getResultList();
Read more about it here.
a couple of things, RefMessage class is using composite primary key so i guess you need to use #IdClass or #EmbeddedId annotation. here I'm providing using
#IdClass
public class RefId implements Serializable {
private String codeSignalement;
private String destinataires;
// default constructor
public RefId() {
}
public RefId(String codeSignalement, String destinataires) {
this.codeSignalement = codeSignalement;
this.destinataires = destinataires;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RefId refId = (RefId) o;
return Objects.equals(codeSignalement, refId.codeSignalement) &&
Objects.equals(destinataires, refId.destinataires);
}
#Override
public int hashCode() {
return Objects.hash(codeSignalement, destinataires);
}
}
then you need to use like follows
#Entity
#Table(name = "DA10004_REF_MESSAGE")
#IdClass(RefId.class)
public class RefMessage {
#Id
#Column(name = "CD_SIGNALEMENT")
public String codeSignalement;
#Id
#Column(name = "DESTINATAIRE")
public String destinataires;
#Column(name = "MESSAGE")
public String message;
}
define your repository as follows:
public interface RefSignalementRepo extends
JpaRepository<RefSignalement, String> {
}
RefSignalement class defination as follows:
#Entity
#Table(name = "DA10003_REF_SIGNALEMENT")
public class RefSignalement {
#Id
#Column(name = "CD_SIGNALEMENT")
public String codeSignalement;
#Column(name = "LIBELLE")
public String libelle;
#Column(name = "CATEGORIE")
public String categorie;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
#JoinColumn(name = "CD_SIGNALEMENT")
public List<RefMessage> refMessages;
}
very example app
#SpringBootApplication
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(App.class, args);
RefSignalementRepo repo = context.getBean(RefSignalementRepo.class);
RefSignalement obj = new RefSignalement();
obj.codeSignalement = "1";
obj = repo.save(obj);
obj.refMessages = new ArrayList<>();
RefMessage message = new RefMessage();
message.codeSignalement = "1";
message.destinataires = "2";
message.message = "custom message";
obj.refMessages.add(message);
obj = repo.save(obj);
List<RefSignalement> objs = repo.findAll();
System.out.println(objs.get(0).refMessages.size());
EntityManager em = context.getBean(EntityManager.class);
List<RefSignalement> listRefSignalement = em.createQuery("SELECT p FROM RefSignalement p, RefMessage m", RefSignalement.class).getResultList();
System.out.println(listRefSignalement.get(0).refMessages.size());
}
}
I'am trying to provide Application on Spring with Lazy-Fetch relation between Entities.
Model "User":
Entity
#Table(name = "users")
#Component
public class User {
#Id
#SequenceGenerator(name = "userseq", sequenceName = "userseq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "userseq")
private Integer id;
// Some fields/getters/setters
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private List<Tracker> trakers;
}
Model "Trackers"
#Entity
#Table(name = "trackers")
#Component
public class Tracker {
#Id
#SequenceGenerator(name = "tracker_seq", sequenceName = "tracker_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tracker_seq")
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", insertable=false, updatable=false)
#OnDelete(action = OnDeleteAction.CASCADE)
private User user;
// Some fields/getters/setters
}
Inherited JPA-repository with #EntityGraph. By this I'am trying to provide select user with all trackers, related with:
#Repository
#Transactional(readOnly = true)
public interface CrudUserRepository extends JpaRepository<User, Integer> {
#EntityGraph (attributePaths = {"trackers"}, type = EntityGraph.EntityGraphType.LOAD)
#Query("SELECT u FROM User u WHERE u.id=?1")
User getByIdWithTrackers(int id);
}
Repository-class:
#Repository
public class AnketUserRepository implements UserRepository {
#Autowired
private CrudUserRepository crudRepository;
#Override
public User getByIdWithoutTrackers(int id) {
return crudRepository.findById(id).orElse(null);
}
#Override
public User getByIdWithTrackers(int id){
return crudRepository.getByIdWithTrackers(id);
}
}
And controller:
#RestController ("userRestController")
#RequestMapping(value = UserRestController.USER_URL, produces =
MediaType.APPLICATION_JSON_VALUE)
public class UserRestController extends AbstractUserController {
public static final String USER_URL = "/customers";
#GetMapping("/{id}")
public User getByIdWithoutTrackers(#PathVariable int id) {
return super.getByIdWithoutTrackers(id);
}
#GetMapping("/{id}/withTrackers")
public User getByIdWithTrackers(#PathVariable int id) {
return super.getByIdWithTrackers(id);
}
}
Query "/customers/1" works fine. It returns all customers without trackers (Lazy-Fetch, accordingly).
But "/customers/1/withTrackers" returns the following exception:
lang.IllegalArgumentException: Unable to locate Attribute with the the given name [trackers] on this ManagedType [ru.spb.model.User]"}
Oh, its a stupid mistake. In User I write "trakers". But in CrudUserRepository at attributePaths "traCkers".
I am building a rest api on Jhipster that must return the billers details using a Category ID as the search parameter. The call to the categories endpoint is returning list of categories but the call to the billers endpoint using one of the category id is returning a null result .
public interface ApplicationUrl {
String BILLERS = "/category/{categoryid}";
}
This is the controller :
#RequestMapping(ApplicationUrl.BASE_CONTEXT_URL)
public class BillingGatewayController {
#Autowired
private BillingGatewayService billingGatewayService;
#GetMapping(ApplicationUrl.BILLERS)
public BillersServiceResponse getAllBillersByCatId(#PathVariable Long categoryId) {
BillersServiceResponse defaultServiceResponse = new BillersServiceResponse();
defaultServiceResponse.setMessage(Message.fail.name());
ResponseCode responseCode = ResponseCode.BILLER_NOT_AVAILABLE;
log.debug("REST request to get all billers");
List<BillerDto> billers = billingGatewayService.findBillers(categoryId);
if (CollectionUtils.size(billers) > 0) {
responseCode = ResponseCode.SUCCESSFUL;
defaultServiceResponse.setStatus(responseCode.getCode());
defaultServiceResponse.setMessage(Message.SUCCESSFUL.name());
defaultServiceResponse.setData(billers);
}
defaultServiceResponse.setStatus(responseCode.getCode());
return defaultServiceResponse;
}
}
This is the service classes :
public interface BillingGatewayService {
List<BillerDto> findBillers(Long id);
}
public interface BillersRepository extends JpaRepository<Billers, Long>, JpaSpecificationExecutor<Billers> {
}
#Service("billingGatewayService")
public class BillingGatewayServiceImpl implements BillingGatewayService {
#Autowired
private ExtBillersRepository billersRepository;
#Override
public List<BillerDto> findBillers(Long categoryId) {
BillerResponseDto billerResponseDto = new BillerResponseDto();
List<BillerDto> billers = billersRepository.findAllActiveBillers(categoryId);
billerResponseDto.setBillers(billers);
billerResponseDto.setCategoryId(String.valueOf(categoryId));
return billers;
}
}
import com.fms.payfuze.dto.BillerDto;
import com.fms.payfuze.repository.BillersRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface ExtBillersRepository extends BillersRepository {
String ACTIVE_BILLERS = "select new com.fms.payfuze.dto.BillerDto(b.id, b.name) from Billers b inner join b.categories c where c.id=b.id order by b.name";
#Query(ACTIVE_BILLERS)
List<BillerDto> findAllActiveBillers(#Param("id") Long id);
}
This is the billerDTO :
public class BillerDto {
private String billerId;
private String nameOfBiller;
public BillerDto(Long id, String name) {
this.billerId = String.valueOf(id);
this.nameOfBiller = name;
}
public String getBillerId() {
return billerId;
}
public void setBillerId(String billerId) {
this.billerId = billerId;
}
public String getNameOfBiller() {
return nameOfBiller;
}
public void setNameOfBiller(String nameOfBiller) {
this.nameOfBiller = nameOfBiller;
}
}
and this is the Billers class:
#Entity
#Table(name = "billers")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Billers implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "active")
private Boolean active;
#Column(name = "date_created")
private Instant dateCreated;
#OneToOne
#JoinColumn(unique = true)
private BillersRouterConfig billersRouterConfig;
#OneToMany(mappedBy = "billers")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<TransactionDetails> billers = new HashSet<>();
#ManyToMany(mappedBy = "billers")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JsonIgnore
private Set<Categories> categories = new HashSet<>();
Getters and setters
I have been on it for days and been brain storming , I'll appreciate all inputs but constructive and reconstructive . Thanks
You're passing #Param("id") in your repository but you are not actually using that id in your SQL Query. Your where condition only has a join statement. You need to add AND c.id = :id after joining the table so you can mention which category you want to get by category id.
Also, it should be WHERE c.biller_id = b.id in your JOIN statement?
Try something like this
String ACTIVE_BILLERS =
"select new com.fms.payfuze.dto.BillerDto(b.id, b.name)
from Billers b inner join
b.categories c
where c.billers_id = b.id and c.billers_id = :id
order by b.name";
I have two tables:
Employee
id
firstName
lastName
.
.
.
Training
id
employeeId
trainingName
trainingSuspsnseDate
trainingComplete
When I perform a standard SQL query in MySQL Workbench, it looks like this:
SELECT e.id, e.firstName, e.lastName, t.trainingName, t.trainingSuspenseDate, t.trainingComplete
FROM Employee e
JOIN Training t on t.employeeId = e.id
WHERE t.trainingSuspenseDate < CURDATE()
order by t.trainingSuspenseDate;
Now, I want to create a criteria query of the same SQL query, but I am having trouble with the join. This is what I have tried based on my googling:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> employeeQuery = builder.createQuery(Employee.class);
Root<Employee> employee = employeeQuery.from(Employee.class);
Join<Employee, Training> training = employee.join(Employee_.ID);
employeeQuery.select(builder.construct(Employee.class,
employee.get(Employee_.ID),
employee.get(Employee_.firstName),
employee.get(Employee_.lastName),
training.get(Training_trainingName),
training.get(Training_trainingSuspsnseDate),
training.get(Training_trainingComplete)));
However, I am getting the error:
incompatible types: inference variable Y has incompatible equality constraints Templates,Integer where Y,X are type-variables:
Y extends Object declared in method <Y>join(SingularAttribute<? super X,Y>)
X extends Object declared in interface From
I have tried other permutations of the JOIN, but I get different errors. I cannot seem to find the exact "secret" to creating this query.
Join<Employee, Training> training = training.join(Training_.employeeId);
or
Join<Employee, Training> training = training.join(Training_.employeeId).join(Employee_.ID);
or
Join<Training, Employee> training = training.join(Training_.employeeId);
or
Join<Training, Employee> training = training.join(Training_.employeeId).join(Employee_.ID);
or
.
.
.
EDIT: Added my model classes
Employee.java
#Entity
#Table(name = "employee")
#XmlRootElement
#NamedQueries(
{
#NamedQuery(name = "Employee.findAll", query = "SELECT e FROM Employee e"),
#NamedQuery(name = "Employee.deleteAll", query = "DELETE FROM Employee e"),
#NamedQuery(name = "Employee.countAll", query = "SELECT COUNT(e.ID) FROM Employee e")
})
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#GeneratedValue
#Column(name = "id")
private Integer ID;
#Basic(optional = true)
#Column(name = "name_first")
private String firstName;
#Basic(optional = true)
#Column(name = "name_last")
private String lastName;
#Basic(optional = true)
#Column(name = "created_date")
private String employeeDate;
#Basic(optional = true)
#Column(name = "personal_type")
private String personnelType;
public Employee() {
ID = 0;
}
public Employee(Integer id) {
this.ID = id;
}
public Integer getID() {
return ID;
}
public void setID(Integer id) {
this.ID = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmployeeDate() {
return employeeDate;
}
public void setEmployeeDate(String employeeDate) {
this.employeeDate = employeeDate;
}
public String getPersonnelType() {
return personnelType;
}
public void setPersonnelType(String personnelType) {
this.personnelType = personnelType;
}
}
Training.java
#Entity
#Table(name = "training")
#XmlRootElement
#NamedQueries(
{
#NamedQuery(name = "Training.findAll", query = "SELECT t FROM Training t"),
#NamedQuery(name = "Training.deleteAll", query = "DELETE FROM Training t"),
#NamedQuery(name = "Training.countAll", query = "SELECT COUNT(t.ID) FROM Training t")
})
public class Training implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#GeneratedValue
#Column(name = "ID")
private Integer ID;
#Basic(optional = false)
#Column(name = "employee_id")
private String employeeId;
#Basic(optional = false)
#Column(name = "training_name")
private String trainingName;
#Basic(optional = false)
#Column(name = "training_suspense_date")
private Date trainingSuspenseDate;
#Basic(optional = false)
#Column(name = "training_complete")
private Boolean trainingComplete;
public Integer getID() {
return ID;
}
public void setID(Integer ID) {
this.ID = ID;
}
public String getEmployeeId() {
return employeeId;
}
public void setEmployeeId(String employeeId) {
this.employeeId = employeeId;
}
public void setTrainingName(String trainingName) {
this.trainingName = trainingName;
}
public String getTrainingName() {
return trainingName;
}
public void setTrainingSuspenseDate(Date trainingSuspsenseDate) {
this.trainingSuspsenseDate = trainingSuspsenseDate;
}
public Date getTrainingSuspenseDate() {
return trainingSuspsenseDate;
}
public void setTrainingComplete(Boolean trainingComplete) {
this.trainingComplete = trainingComplete;
}
public Boolean getTrainingComplete() {
return trainingComplete;
}
}
I can figure out that You have a meta-model generated for your query.
So the best way will be to extends your entity definition as follow:
You have to add mapping in your Training class:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "employeeId")
private Employee employee;
Then in your Employee class add oposite reference:
#OneToMany(mappedBy = "employee")
private Set<Training> trainings = new HashSet<>();
Then change your criteria query to:
Join<Employee, Training> training = employee.join(Employee_.trainings);
You can try cross join. Native sql is a bit differerent but the result is as axpected
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> employeeQuery = builder.createQuery(Employee.class);
Root<Employee> employee = employeeQuery.from(Employee.class);
Root<Employee> training= employeeQuery.from(Training.class);
employeeQuery.select(builder.construct(Employee.class,
employee.get(Employee_.ID),
employee.get(Employee_.firstName),
employee.get(Employee_.lastName),
training.get(Training_.trainingName),
training.get(Training_.trainingSuspsnseDate),
training.get(Training_.trainingComplete)))
.where(builder.equal(employee.get(Employee_.ID), training.get(Training_.employeeId)));
Looking at your model classes, the entities are not related directly (even though the employeeId in Training is supposed to be a foreign key, it is not defined as such in the entity relationship. So, if you wish to work with the existing entities, without changing them, you would need the following -
A POJO (for example EmpRes) which maps the attributes as per the select clause. criteriaQuery should be initialized on this POJO as -
CriteriaQuery<EmpRes> criteriaQuery = builder
.createQuery(EmpRes.class);
As the entities are unrelated, the generated query will have a cross join.
The code would look like -
criteriaQuery.select(builder.construct(EmpRes.class, employee
.get(Employee_.getAttribute("ID").getName()), employee
.get(Employee_.getAttribute("firstName").getName()), employee
.get(Employee_.getAttribute("lastName").getName()), training
.get(Training_.getAttribute("trainingName").getName()),
training.get(Training_.getAttribute("trainingSuspenseDate")
.getName()), training.get(Training_.getAttribute(
"trainingComplete").getName())));
criteriaQuery.where(builder.equal(employee.get("ID"), training.get("employeeId")));
List<EmpRes> employees = entityManager.createQuery(criteriaQuery).getResultList();
However, if the entities can be changed (as should be the ideal design), an Employee has Training(s). So, a #OneToMany relationship between Employee and Training model classes should be defined as follows -
Employee.java
#OneToMany(mappedBy="employee")
private Set<Training> trainings = new HashSet<>();
Training.java
#ManyToOne
#JoinColumn(name = "employeeId")
private Employee employee;
CriteriaQuery related code -
Join<Employee, Training> trainingJoin = employee.join(Employee_.getAttribute("trainings").getName());
criteriaQuery.select(builder.construct(EmpRes.class, employee
.get(Employee_.getAttribute("ID").getName()), employee
.get(Employee_.getAttribute("firstName").getName()), employee
.get(Employee_.getAttribute("lastName").getName()),
trainingJoin.get(Training_.getAttribute("trainingName")
.getName()), trainingJoin.get(Training_.getAttribute(
"trainingSuspenseDate").getName()), trainingJoin
.get(Training_.getAttribute("trainingComplete")
.getName())));
You can then add the additional where clause based on your requirements.
A good reference to Criteria API is here.
This error message - incompatible types: inference variable Y has incompatible equality constraints - is an indication you need to carefully review DATA TYPES of the columns you are joining. The should be same data types on both sides of the = for performance and high speed comparisons.
I have the following
#Entity
#Table(name = "PROJECTS")
public class Project implements Serializable {
#Id
private Integer SlNo;
#Id
private Long projectNo;
private Date projectDate;
}
and in DAO class
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> countQ = cb.createQuery(Long.class);
Root<Project> empCount = countQ.from(Project.class);
countQ.select(cb.count(empCount));
TypedQuery<Long> countquery = entityManager.createQuery(countQ);// error in this line
I am getting exception java.lang.IllegalStateException: No supertype found in the above line. How can I resolve or workaround this issue? Looks like there is a bug, are there any solution to this?
I am using Hibernate 4.1.0.Final
I have resolved the issue by using #EmbeddedId in Entity class and #Embeddable in Primary Key class.
#Entity
#Table(name = "PROJECTS")
public class Project {
#Column(name = "SL_NO" , insertable = false, updatable = false)
private Integer SlNo;
#Column(name = "PROJECT_NO" , insertable = false, updatable = false)
private Long projectNo;
private Date projectDate;
#EmbeddedId
ProjectPK projectPK;
and Primary Key class
#Embeddable
public class ProjectPK implements Serializable {
#Column(name = "SL_NO")
private Integer SlNo;
#Column(name = "PROJECT_NO")
private Long projectNo;
//with hashCode and equals implementation
for the case Using #EmbeddedId, here is my solution. This code I have written in one class itself, in Entity class.
Class MyEntity - It is my actual Entity class for my table. "OtherFields" are those fields which are not part of primary key.
Class MyEntityPrimaryKeys - It is the class made for my composite key which makes a primary key for my "MyEntity" class. Here ROLLNO and AGE together makes a primary key.
MyEntity.java
#Entity
#Table(name = "myTable")
public class MyEntity extends GenericPersistableEntity implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
MyEntityPrimaryKeys id;//Composite Primary key
//Composite fields can be declared here for getter and setters
#Column(name = "ROLLNO")
private Long RollNo;
//Composite fields can be declared here for getter and setters
#Column(name = "AGE")
private Long age;
#Column(name = "OtherFields"
private Long OtherFields;
//getter and setters comes here
}
#Embeddable
class MyEntityPrimaryKeys implements Serializable{
private static final long serialVersionUID = 1L;
#Column(name = "ROLLNO")
Long RollNo;
#Column(name = "AGE")
Long age;
#Override
public int hashCode() {
HashCodeBuilder hcb = new HashCodeBuilder();
hcb.append(RollNo);
hcb.append(age);
return hcb.toHashCode();
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof MyEntityPrimaryKeys)) {
return false;
}
MyEntityPrimaryKeys that = (MyEntityPrimaryKeys) obj;
EqualsBuilder eb = new EqualsBuilder();
eb.append(RollNo, that.RollNo);
eb.append(age, that.age);
eb.append(tonMonth, that.tonMonth);
eb.append(tonYear, that.tonYear);
return eb.isEquals();
}
}