add Nhibernate Projections - nhibernate

Have Employee: BaseEntity class with
public virtual BaseEntity Department {get;set;}
public virtual BaseEntity Position {get;set;}
and BaseEntity class:
public virtual long Id {get;set;}
public virtual long Name {get;set;}
How can I map my entities and /create select like this
SELECT DepartmentTable.nvarchar1 as Department, PositionTable.nvarchar1 as Position, COUNT(*) as N
FROM AllUserData EmployeeTable
left outer join AllUserData PositionTable on EmployeeTable.int2=PositionTable.tp_ID
left outer join AllUserData DepartmentTable on EmployeeTable.int3=DepartmentTable.tp_ID
WHERE EmployeeTable.tp_ListId = #p0 and PositionTable.tp_ListId = #p2 and DepartmentTable.tp_ListId = #p1
GROUP BY DepartmentTable.nvarchar1, PositionTable.nvarchar1;
I tried like this:
var criteria = session.CreateCriteria<Entities.Employee>();
criteria.CreateAlias("Position", "PositionTable", JoinType.LeftOuterJoin);
criteria.Add(Restrictions.Or(Restrictions.Eq("PositionTable.ListId", Program.PositionListGuid), Restrictions.IsNull("PositionTable.Id")));
criteria.CreateAlias("Department", "DepartmentTable", JoinType.LeftOuterJoin);
criteria.Add(Restrictions.Or(Restrictions.Eq("DepartmentTable.ListId", Program.DepartmentListGuid), Restrictions.IsNull("DepartmentTable.Id")));
criteria.Add(Restrictions.Eq("ListId", Program.EmployeeListGuid));
var projectionList = Projections.ProjectionList();
projectionList.Add(Projections.RowCount());
projectionList.Add(Projections.GroupProperty("Department"), "AliasedId");
criteria.SetProjection(projectionList);
criteria.SetResultTransformer(Transformers.AliasToBean(typeof(BaseEntity)));
var list = criteria.List<BaseEntity>();
// "More than one row with the given identifier was found" exception raised here
I'am confused, should I somehow use joined table alias for Projections.GroupProperty, rework my mapping/Entity to use only primitive types (string, int) or any other approach?

Figured out:
Add int Number to BaseEntity and string DepartmentName, string PositionName to Employee. Now I can use Projections to map Sql grouped by result to this properties:
var pl = Projections.ProjectionList()
.Add(Projections.RowCount(), GetEntityProperty(typeof (BaseEntity), "Number"))
.Add(Projections.GroupProperty("DepartmentTable.Title"), "PositionName")
.Add(Projections.GroupProperty("PositionTable.Title"), "DepartmentName");
ICriteria criteria = session.CreateCriteria<Employee>()
.CreateAlias("Position", "PositionTable", JoinType.LeftOuterJoin)
.Add(Restrictions.Eq("PositionTable.ListId", Program.PositionListGuid))
.CreateAlias("Department", "DepartmentTable", JoinType.LeftOuterJoin)
.Add(Restrictions.Eq("DepartmentTable.ListId",Program.DepartmentListGuid))
.Add(Restrictions.Eq("ListId", Program.EmployeeListGuid))
.SetProjection(pl)
.SetResultTransformer(Transformers.AliasToBean<BaseEntity>());
var list = criteria.List<BaseListEntity>().ToList();

Related

Spring Data JPA Query for inner join table throwing error

Spring DATA JPA question... I am trying to write a query to access the data in my sql join table.
I have my join table set up as follows:
#Entity
#Table(name="WritingCompany")
public class WritingCompany {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "companyId")
private Long id;
// private String companyName;
#ManyToMany
#JoinTable(name = "Join-WritingCo-Carrier",
joinColumns = #JoinColumn(name="writingCo"),
inverseJoinColumns = #JoinColumn(name="carrier")
)
private Set<CarrierAppointment> carriers;
//...getters and setters
}
public class CarrierAppointment {
// ...
#ManyToMany(
cascade=CascadeType.ALL,
mappedBy = "companyName"
)
private Set<WritingCompany> companies;
public Set<WritingCompany> getCompanies() {
return companies;
}
public void setCompanies(Set<WritingCompany> companies) {
this.companies = companies;
}
//...
}
I am unsure which class repository I need to write the query for... I am trying to find all the writingCo names from the join table that all have the same carrier id that matches one specific carrier.
The Join Table is set up through writing company but I feel like it should be accessed through CarrierAppointment Repository since I am matching carrier Id's.
This is what I've tried in the CarrierAppointmentrepository and it throws this error (unexpected token: Join near line 1, column 94):
#Query("Select companyName FROM WritingCompany INNER JOIN CarrierAppointment ON Join-WritingCo-Carrier.carrier= CarrierAppointment.CarrierAppointmentId")
List<CarrierAppointment> findCarrierAppoinmentFromJoin(CarrierAppointment carrier);
I've also tried:
#Query("SELECT writingCo FROM Join-WritingCo-Carrier WHERE carrier= CarrierAppointment.CarrierAppointmentId")
List<CarrierAppointment> findCarrierAppoinmentFromJoin(CarrierAppointment carrier);
Then I tried this in the writingCompanyRepository which also throws a similar error:
#Query("Select companyName FROM WritingCompany INNER JOIN CarrierAppointment ON Join-WritingCo-Carrier.carrier= CarrierAppointment.CarrierAppointmentId")
List<WritingCompany> findAllWithDescriptionQuery(CarrierAppointment carrier);
I am having a hard time understanding what this query is saying. Do I ever need to access the columns from the sql join table, or am I just querying around the join table by asking for each class that is making up the join columns in the join table? What is the right part of the statement, after INNER JOIN stating ? Could someone please provide a deeper explanation of why the query is written so I can figure out why it's not working? I've been reading a lot of inner join examples and just can't seem to figure it out.

Hibernate createNativeQuery returns duplicate rows

I have 2 database tables Customer and Items with 1 -> many relation. To fetch data from database i am using the following query.
select customer.id, customer.name, items.itemName, items.itemPrice from testdb.customer INNER JOIN items ON items.customer_Id = customer.id
I have an entity class Customers
#Entity
public class Customer{
#Id
private int id;
#Column
private String name;
#Column
private String itemName;
#Column
private int itemPrice;
public Customer() {}
//Getter and setter are here
.......
}
in Service class i have the following code.
#GET #Path("/getCustomerInfo")
#Produces(MediaType.APPLICATION_JSON)
public List getCustomerInfo() {
CustomerDao dao = new CustomerDao();
return dao.getBuildingsCustomerInfo();
}
in my DAO class i have the following code
public List<Customer> getCustomerInfo(){
Session session = SessionUtil.getSession();
String queryString = "the above mentioned query";
List<Customer> customerInfo = session.createNativeQuery(queryString, Customer.class) ;
session.close();
return customerInfo;
}
I am getting the following JSON response from the service
[id:1, name:"Alfred", itemName:"jeans", itemprice:10],[id:1, name:"Alfred", itemName:"jeans", itemprice:10],[id:2, name:"James", itemName:"watch", itemPrice:20 ],[id:2, name:"James", itemName:"watch", itemPrice:20 ], [id:2, name:"James", itemName:"watch", itemPrice:20 ]
The number of results are 5 which is correct But 2nd result is a copy of 1st, 4th and 5th are copies of 3rd. In 2nd, 4th and 5th results the itemName and the itemPrice should be different.
if I use createSQLQuery(queryString); instead of createNativeQuery(queryString, Customer.class); I am getting the correct result but without entity attribut names.
[1, "Alfred", "jeans", 10],[1, "Alfred", "shirt", 15],[2, "James", "watch", 20], [2, "James", "coffee", 25], [2, "James", "drinks", 30]
I have seen number of articles but could not find the solution. I have to use createNativeQuery() not createSQLQuery() because I need to map the entity class attributes. Please let me know if i am doing something wrong.
Your data structure is wrong on the Java side and not corresponding to the database relation. In the relation you describe you need to have a list of items:
#Entity
public class Customer implements Serializable {
// ... the fields you have so far
// assuming the parent field on the other side is called customer
// you may also want to set the cascade and orphanRemoval properties of the annotation
#OneToMany(mappedBy = "customer")
#JsonManagedReference // assuming you're using Jackson databind JSON
private List<Item> items;
}
And on the Item side:
#Entity
public class Item implements Serializable {
#Id
private int id;
#JsonBackReference
#ManyToOne
#JoinColumn(name = "customer_Id")
private Customer customer;
}
Then if you really the JSON data strucutred that way, you need a third Entity class to use as a ResultSetMapping.
#Entity
#SqlResultSetMapping(
name = "CustomerItem",
entities = #EntityResult(entityClass = CustomerItem.class)
)
#NamedNativeQueries({
#NamedNativeQuery(
name = "CustomerItem.getAll",
resultSetMapping = "CustomerItem"
query = "select customer.id as cid, items.id as iid, customer.name,"
+ " items.itemName, items.itemPrice from testdb.customer INNER JOIN"
+ " items ON items.customer_Id = customer.id"
)
})
public class CustomerItem implements Serializable {
#Id
private int cid;
#Id
private int iid;
#Column
private String name;
#Column
private String itemName;
#Column
private int itemPrice;
... getters and setters
}
Then you can use the native query in named variant, which should offer some slight optimizations.
List<CustomerItem> lst = em.createNamedQuery("CustomerItem.getAll", CustomerItem.class)
.getResultList();
The use of #SqlResultSetMapping is so that the returned entities are not monitored for changes, but you can still use the defined entity for the result. I believe that by JPA specification it should also work without it, but in Hibernate it doesn't. Could be a bug, or a planned, but not implemented feature, or I could just be misinterpreting the JPA usage, but this workaround does work with Hibernate 5+.
Not sure about the exact reason behind duplicates but SELECT DISTINCT will solve your issue as it will take only distinct records.
Refer using-distinct-in-jpa
I solve this issue by using #SqlResultSetMapping

Hibernate SQL query matching more than one members of an ElementCollection

I have Pojo mapped with JPA annotations like this
#Entity
#Table(name = "record")
public class SearchRecord {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private int id;
private String vidName;
#ElementCollection
private List<String> videoLabels = new ArrayList<String>();
and would like to run a Hibernate query that filters out all SearchRecords that match/contain 1..n videoLabels. (only objects that match all of the videoLabels)
I was able to search for SearchRecords that match a single label by running the following query:
String labelCar = "Car";
String labelPerson = "Person";
TypedQuery<SearchRecord> query = em.createQuery("SELECT b FROM SearchRecord b JOIN b.videoLabels l WHERE l = :param1",SearchRecord.class);
query.setParameter("param1", labelCar);
List<SearchRecord> results = query.getResultList();
But how can I execute a query filtering out all SearchResults matching Car and Person?
Thanks for your support
I was able to solve the problem with the following query
SELECT DISTINCT a FROM SearchRecord a JOIN a.labels b JOIN a.labels c WHERE b.name = 'Car' and c.name = 'Person'

join 2 columns and get only matched vlaues in hibernate

I have 2 tables:
course: id, name
student: id, course_id, age
Tables are tied with oneToMany annotation
I am trying to write a hibernate query against course that will return courses and students that are x years 0ld. That's my attempt:
SELECT c from Course c RIGHT JOIN c.student p ON p.age = 20
It returns a course that has at least one student that is 20. when there are more students that are not 20, they are included in the result set as well. Is there a way to restrict the joined table values?
Edit:
These are my entities:
public class Course implements Serializable {
private static final long serialVersionUID = 646349111982187812L;
#Id
#Column(name = "id", unique=true, nullable=false)
private String id;
#Column(name = "name", unique=false, nullable=false)
private String name;
#OneToMany(cascade={CascadeType.ALL, CascadeType.REMOVE},
fetch=FetchType.LAZY, mappedBy = "Course")
#OrderBy("age")
#JsonManagedReference
private Set<Student> students;
getters and setters ...
}
public class Student implements Serializable {
private static final long serialVersionUID = 646349221337813L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "age", unique=false, nullable=true)
private String age;
#ManyToOne()
#JsonBackReference
private Course course;
}
I am trying to use this method from my crudrepository to get the expected result:
#Query(?????)
public List<Course> findCoursesWithAdults(#Param("age")int age)
it sounds like you want an inner join instead of a right join
SQL
SELECT distinct c.* from Course c inner join Student p on (c.id=p.course_id and p.age=20)
or with hql
select distinct c from Course c inner join c.student p with p.age=20
in order to get the information you are requesting, you will either have to limit the Set in Course using #Where ("age = 20" ) or simply deal with a list of students instead of courses.
select p from Course c inner join c.student p with p.age=20
you can reference the attached course object through any one of the getCourse methods on the student objects. You could also use "group by" here to help you order things.
or you can use a filter within the hibernate Entity...
#Transient
public List<Students> getStudents(Integer age){
List<Students> tmp = new ArrayList<>();
for(Student s: getStudents())
if(s.getAge().equals(age))tmp.add(s);
return tmp;
}
one more edit and im done... there is another way to do this that i didnt mention..
a select wrapper query.
create a wrapper object for your course and student
public class CourseWrapper(){
private Course c;
private List<Students> p = new ArrayList<>();
....constructor ... getters ...setters
}
select new CourseWrapper(c, students)
from Course c
left outer join c.students p with p.age=20'
more info here
SELECT c.name, s.id
FROM course c
INNER JOIN student s ON c.id = s.course_id
WHERE s.age = 20

Fluent Nhibernate join on non foreign key property

I can't find this anywhere, but it seems pretty trivial. So, please excuse if this is a duplicate.
I have something like:
public class Doctor : Entity
{
...some other properties here...
public virtual string Email { get; set; }
}
public class Lawyer : Entity
{
...some other properties here...
public virtual string Email { get; set; }
}
I want to return all doctors where there is no email match in the Lawyers table like:
select * from Doctors d
where d.Email not in
(select l.Email from Lawyers l where l.Email is not null)
or using a join:
select d.* from Doctors d
left join Lawyers l on l.Email = d.Email
where l.Email is null
The problem is that the Email is of course not set up as a foreign key. I have no mapped property on the Doctor entity that maps to Lawyer.
What I've tried so far:
ICriteria criteria = Session.CreateCriteria(typeof(Doctor))
.CreateAlias("Lawyers.Email", "LawyerEmail", JoinType.LeftOuterJoin)
.Add(Restrictions.IsNull("LawyerEmail"));
return criteria.List<Doctor>();
But, I get a "cannot resolve property Lawyer of MyPlatform.MyNamespace.Doctor" error. Any ideas how to set up my DoctorMap and adjust the criteria tomfoolery to achieve this?
NHibernate for the loss........Entity Framework for the win....
We can achieve that with a feature called subquery:
// a inner SELECT to return all EMAILs from Lawyer table
var subQuery = DetachedCriteria.For<Lawyer>()
.SetProjection(Projections.Property("Email"));
// the root SELECT to get only these Doctors
var criteria = session.CreateCriteria<Doctor>();
// whos email is not in the sub SELECT
criteria.Add(Subqueries.PropertyNotIn("Email", subQuery));
// get first 10
var result = criteria
.SetMaxResults(10)
.SetFirstResult(0) // paging
.List<Doctor>();