Entity Framework query to custom object Class with List - entity-framework

I am using entity frame work for Web API 2.0 I am trying to query Author Book to a custom Object.
Object is :
public class AuthorBooks
{
public Author Author;
public List<BookDetail> BookDetails;
public AuthorBooks() {
Author = new Author();
BookDetails = new List<BookDetail>();
}
}
public class BookDetail
{
public BookDetail()
{
Book = new Book();
Category = new Category();
SubCategory = new SubCategory();
}
public Book Book { get; set; }
public Category Category { get; set; }
public SubCategory SubCategory { get; set; }
}
Here Book, Author, Category and SubCategory are entities
Relation presently working properly is:
Author
----List
public class AuthorBooks
{
public Author Author;
public List<Book> Book;
public AuthorBooks() {
Author = new Author();
Book = new List<Book>();
}
}
using query I get proper result :
AuthorBooks authorBooks = (from a in _context.Authors.Where(author => author.Id == id)
join b in _context.Books on a.Id equals b.AuthorName into b1
select new AuthorBooks { Author = a, Book = b1.ToList() }).Single<AuthorBooks>();
But Now for Above Mentioned Relation :
Author
---List
where Book Detail of other Entities related by Id's of Entities Category and SubCategory.
So How Can It be Done with BookDetails Id???
AuthorBooks authorBooks = (from a in _context.Authors.Where(author => author.Id == id)
join b in _context.Books on a.Id equals b.AuthorName into b1
from b2 in b1
join c in _context.Categories on b2.CategoryId equals c.Id
join s in _context.SubCategories on b2.SubCategoryId equals s.Id
select new AuthorBooks { Author = a, BookDetails = #####List<BookDetail>####### }).Single<AuthorBooks>();

You need to join the necessary data needed for BookDetail in a subquery and then group join the authors query to it to get the desired author - list of detail correlation:
AuthorBooks authorBooks = (
from a in _context.Authors.Where(author => author.Id == id)
join bd in (from b in _context.Books
join c in _context.Categories on b.CategoryId equals c.Id
join s in _context.SubCategories on b.SubCategoryId equals s.Id
select new BookDetail { Book = b, Category = c, SubCategory = s }
) on a.Id equals bd.Book.AuthorName into bookDetails
select new AuthorBooks { Author = a, BookDetails = bookDetails.ToList() }
).Single();

Related

Eclipselink problem with subquery in where clause

I have the following structure: (I am using eclipselink 2.6.0)
#Entity
public class A {
#Id
private double id;
...
#OneToMany
private List<B> b;
#OneToMany(mappedBy = "a")
private List<C> c;
...
}
#Entity
public class B {
#Id
private double id;
private boolean hidden;
}
#Entity
public class C {
#Id
private double id;
private String label;
private String value;
#ManyToOne
private A a;
}
I need to find A that are not hidden, and has s1 = value1, s2 = value 2, this is the implementation using criteria:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<A> cq = cb.createQuery(A.class);
Root<A> root = cq.from(A.class);
Join<A, B> property = root.join(A_.b, JoinType.LEFT);
String currentUser = ctx.getCallerPrincipal().getName();
property.on(cb.equal(property.get(B_.creator), currentUser));
C af = new C("value1", "value2");
Subquery<B> sq = cq.subquery(B.class);
Root<C> sqRoot = sq.from(C.class);
sq.where(cb.and(cb.equal(sqRoot.get(C_.A), root),
cb.equal(sqRoot.get(C_.label), af.getLabel()),
cb.like(sqRoot.get(C_.value), "%" + af.getValue() + "%")));
sq.select(sqRoot);
cq.where(cb.and(
cb.exists(sq),
cb.or(cb.isNull(property.get(B_.id)),
cb.equal(property.get(B_.hidden), false))));
cq.select(root).distinct(true);
The generated query is:
SELECT DISTINCT t1.ID
FROM B t0, A t1
WHERE (EXISTS (SELECT 1 FROM C t2 WHERE (((t2.A_ID = t1.ID) AND (t2.LABEL = "value1")) AND t2.VALUE LIKE "%value2%")) AND ((t0.ID IS NULL) OR (t0.HIDDEN = false)))
As you can see, the provider ignores the left Join totally as if it doesn't exist! It doesn't do any join between tow tables A and B..
Note that in the JPA JSR, the query should be correct. It also worked as a named query. But I can't use it here since I have lots of changing conditions...
The query should be:
SELECT DISTINCT t1.ID
FROM A t1 LEFT JOIN B t0 ON t1.ID=t0.A_ID AND t0.creator="currentUserValue"
WHERE (EXISTS (SELECT 1 FROM C t2 WHERE (((t2.A_ID = t1.ID) AND (t2.LABEL = "value1")) AND t2.VALUE LIKE "%value2%")) AND ((t0.ID IS NULL) OR (t0.HIDDEN = false)))
I tried to change eclipseLink to the 2.7.7 release...The query changed a bit, but it still wrong, with no left join between A and B.
I appreciate any help with this problem.

Convert specific query into JPQL or Criteria Builder query

Here is the code for 2 entities (it generates three tables in the database). A Book entity:
#Entity
public class Book {
#Id
private long id;
private String name;
#ManyToMany
private List<Author> authors;
}
An Author entity:
#Entity
public class Author {
#Id
private long id;
#Column(unique=true)
private String name;
}
I'm trying to find books by the list of authors. Here is a sql query:
select book.id, ARRAY_AGG(author.name)
from book
join book_authors ba on book.id=ba.book_id
join author on ba.authors_id=author.id
group by book.id
having ARRAY_AGG(distinct author.name order by author.name)=ARRAY['a1', 'a2']::varchar[]
['a1', 'a2'] is a list of book authors, it must be passed as a parameter. The idea is to aggregate authors and then compare them with the list of passed parameters.
How to rewrite this SQL-query into either a JPQL or CriteriaBuilder query?
#Query("select distinct b from Book b join b.authors a where a.name in(:names)")
List<Book> findByAuthorsNames(#Param("names") List<String> names)
If you want to fetch b.authors use join fetch instead of join
If the exact match is necessary you can use Specification like this
public class BookSpecifications {
public static Specification<Book> byAuthorsNames(List<String> names) {
return (root, query, builder) -> {
Join<Book, Author> author = root.join("authors", JoinType.LEFT);
Predicate predicate = builder.conjunction;
for(String name : names) {
Predicate namePredicate = builder.and(author.get("name"), name);
predicate = builder.and(predicate, namePredicate);
}
return predicate;
}
}
}
BookRepository have to extend JpaSpecificationExecutor.
Usage:
BookRepository repository;
public List<Book> findByAuthorsNames(List<String> names) {
return repository.findAll(BookSpecifications.byAuthorsNames(names));
}

JPA Native Query to map One to Many

I have a complex Oracle query which for simplicity's sake looks like this;
SQL> SELECT d.id AS dept_id,
2 d.name AS dept_name,
3 e.id AS emp_id,
4 e.name AS emp_name,
5 e.dept_id AS emp_dept_id
6 FROM drs2_dept d, drs2_emp e
7 WHERE d.id = e.dept_id (+)
8 /
DEPT_ID DEPT_NAME EMP_ID EMP_NAME EMP_DEPT_ID
---------- ------------------- ---------- -------------- -----------
1 SALES 101 JOHN 1
1 SALES 102 JANE 1
2 ADMIN
My Department class is;
#SqlResultSetMapping(
name = "Department.employeeMapping",
classes = {
#ConstructorResult(
targetClass = Department.class,
columns = {
#ColumnResult(name = "DEPT_ID", type = Integer.class),
#ColumnResult(name = "DEPT_NAME")
}
),
#ConstructorResult(
targetClass = Employee.class,
columns = {
#ColumnResult(name = "EMP_ID", type = Integer.class),
#ColumnResult(name = "EMP_NAME"),
#ColumnResult(name = "EMP_DEPT_ID", type = Integer.class)
}
)
}
)
#NamedNativeQuery(
name = "Department.findAllEmployees",
query = "SELECT d.id AS dept_id, " +
" d.name AS dept_name, " +
" e.id AS emp_id, " +
" e.name AS emp_name " +
" e.dept_id AS emp_dept_id, " +
"FROM drs2_dept d, drs2_emp e " +
"WHERE d.id = e.dept_id (+)",
resultSetMapping = "Department.employeeMapping"
)
#Entity
public class Department {
#Id // JPA will not start without it.
Integer id;
String name;
#OneToMany // JPA will not start without it.
List<Employee> employees = new ArrayList<>();
public Department(Integer id, String name) {
this.id = id;
this.name = name;
}
public Department() {}
// getters and setters
}
My Employee class is;
#Entity
public class Employee {
#Id Integer id;
Integer departmentId;
String name;
public Employee(Integer id, String name, Integer departmentId) {
this.id = id;
this.name = name;
this.departmentId = departmentId;
}
public Employee() {}
// getters and setters
}
Because I am using #ConstructorResult I am able to get the data, but it still in a flat structure, that is to say a List<Object[]> with three entries, each containing [Department, Employee]. So I have to do the following to move the Employee records within their respective Department;
#Component
public class DepartmentDAO {
#PersistenceContext EntityManager entityManager;
public Collection<Department> getAllDepartments() {
Query query = entityManager.createNamedQuery("Department.findAllEmployees");
Map<Integer, Department> map = new HashMap<>();
List<Object[]> list = query.getResultList();
for (Object[] tuple: list) {
Department d = (Department) tuple[0];
if (! map.containsKey(d.getId())) {
map.put(d.getId(), d);
}
d = map.get(d.getId());
Employee e = (Employee) tuple[1];
if (e.getId() != null) {
d.getEmployees().add(e);
}
}
return map.values();
}
}
Whenever I add any additional properties to the #OneToMany I seem to get spurious SQL generated in the Hibernate logs which is incorrect (i.e. non-existent column or table names), but as I stated at the start of this question, I want the native SQL only - I don't want Hibernate to figure out what I am trying to do.
Is there any way to get JPA/Hibernate to put the Employee objects into the Department's list for me?
(
As a sub-note, I have seen this question asked here, but either never answered or answered back in 2011, by which time JPA and Hibernate may have progressed.
I should also add that elsewhere in my project I already have Department and Employee fully mapped for CrudRepository use with #Table and #Column, however their #OneToMany definitions do not depict what I am doing in the above query, hence their absence in my example code.
)
This query does not have any clause that forces it to be implemented with a native.
In fact it is considered a bad practice.
Try this:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Departament> cq = cb.createQuery(Departament.class);
Root<Departament> rootDepartament = cq.from(Departament.class);
Join<Departament,Employee> joinEmployee = rootDepartament.join(Departament_.employees,JoinType.Left);
cq.select(rootDepartament);
List<Departament> result = entityManager.createQuery(cq).getResultList();

How to add & update within entity framework

my Model Entity FrameWork :
public partial class T02
{
public long ID { get; set; }
public int F01 { get; set; }
}
Table : T02
------------------------
id[Key,identity] F01
------------------------
1 a
2 b
====================================================
i have a button > Code
public void Button_Click(object Sender, EventArgs e)
{
T02 T1 = new T02 { ID = 1, F01 = "x" };
T02 T2 = new T02 { ID = 2, F01 = "y" };
T02 T3 = new T02 { ID = 0, F01 = "g" };
T02 T4 = new T02 { ID = 0, F01 = "h" };
// How ID > 1 2 Update in Table &
// because not in table, > g , h > automatic add to Table
// ???
}
my problem is : i not ontime update and if T02.ID not on the table automatic added..
Is it possible that I do added to table Records Without id and i do Update Records With id
i worked c#.NET 4.5 . Thanks
Add this method to your Context class:
protected virtual void InsertOrUpdate<T>(T entity, long id)
{
if (id == default(int))
this.Set<T>().Add(entity);
else
this.Entry(entity).State = EntityState.Modified;
}
Which can be used something like this:
public void Button_Click(object Sender, EventArgs e)
{
T02 T1 = new T02 { ID = 1, F01 = "x" };
T02 T2 = new T02 { ID = 2, F01 = "y" };
T02 T3 = new T02 { ID = 0, F01 = "g" };
T02 T4 = new T02 { ID = 0, F01 = "h" };
using(var context = new MyDbContext())
{
context.InsertOrUpdate(T1, T1.ID);
context.InsertOrUpdate(T2, T2.ID);
context.InsertOrUpdate(T3, T3.ID);
context.InsertOrUpdate(T4, T4.ID);
}
}
Once this is working you can consider implementing a standard interface across all of your POCO classes that exposes the ID property so that you no longer have to pass ID into the method:
public interface IId
{
long ID { get; }
}
public partial class T02 : IId
{
public long ID { get; set; }
public int F01 { get; set; }
}
which gives you a neater method in your Context
protected virtual void InsertOrUpdate<T>(T entity) where T : IId
{
if (entity.ID == default(int))
this.Set<T>().Add(entity);
else
this.Entry(entity).State = EntityState.Modified;
}

selecting existing and non existing joined entity at once

I have a join table looks like this.
EXCHANGE_RATE
------------------------------
ID BIGINT PK
SOURCE_CURRENCY_ID BIGINT FK
TARGET_CURRENCY_ID BIGINT FK
Mapped ExchangeRate.java
public class ExchangeRate {
protected ExchangeRage() {
this(null, null);
}
// for SELECT NEW
public ExchangeRate(Currency sourceCurrency, Currency targetCurrency) {
this.sourceCurrency = sourceCurrency;
this.targetCurrency = targetCurrency;
}
#Id private Long id;
#ManyToOne private Currency sourceCurrency;
#ManyToOne private Currency targetCurrency;
}
How can I list all existing and non existing ExchangeRates for given sourceCurrency?
For example,
There are three Currencies. A, B, and C.
There is already an ExhangeRate from A to B
With following method, if sourceCurrency is A,
public List<ExchangeRate> listExchangeable(Currency sourceCurrency) {
//
}
How can I get following list?
ExchangeRate {
id: 0
sourceCurrency: A
targetCurrency: B
},
ExchangeRate {
id: NULL
sourceCurrency: A
targetCurrency: C
}
Required Query
// get all exchange rates for a source currency
public List<ExchangeRate> listExchangeable(Currency sourceCurrency) {
Query qry = em.createQuery("select r from ExchangeRate r where
r.sourceCurrency = :sourceCurrency");
qry.setParameter("sourceCurrency", sourceCurrency);
return qry.getResultList();
}
for non existing ExchangeRates for given sourceCurrency
Query qry = em.createQuery("select r from ExchangeRate r where
r.sourceCurrency != :sourceCurrency");