i need to retrieve single row from table, and i was interested what approach is better.
On the one side getSingleResult is designed for retrieving single result, but it raises exception. Does this method have benefit in performance related to getResultList with
query.setFirstResult(0);
query.setMaxResults(1);
According to Effective Java by Joshua Bloch:
Use checked exceptions for conditions from wich the caller can
reasonably be expected to recover. Use runtime exceptions to indicate
programming errors.
Credit to the source: Why you should never use getSingleResult() in JPA
#Entity
#NamedQuery(name = "Country.findByName",
query = "SELECT c FROM Country c WHERE c.name = :name"
public class Country {
#PersistenceContext
transient EntityManager entityManager;
public static Country findByName(String name) {
List<Country> results = entityManager
.createNamedQuery("Country.findByName", Country.class)
.setParameter("name", name).getResultList();
return results.isEmpty() ? null : results.get(0);
}
}
getSingleResult throws NonUniqueResultException, if there are multiple rows. It is designed to retrieve single result when there is truly a single result.
The way you did is fine and JPA is designed to handle this properly. At the same time, you cannot compare it against getSingleResult any way, since it won't work.
However, depend on the code you are working on, it is always better to refine the query to return single result, if that's all what you want - then you can just call getSingleResult.
There is an alternative which I would recommend:
Query query = em.createQuery("your query");
List<Element> elementList = query.getResultList();
return CollectionUtils.isEmpty(elementList ) ? null : elementList.get(0);
This safeguards against Null Pointer Exception, guarantees only 1 result is returned.
getSingleResult throws NonUniqueResultException, if there are multiple rows or no any rows . It is designed to retrieve single result when there is truly a single result.
In combination with fetch() the usage of setMaxResults(1) can lead to a partially initialised objects. For example,
CriteriaQuery<Individual> query = cb.createQuery(Individual.class);
Root<Individual> root = query.from(Individual.class);
root.fetch(Individual_.contacts);
query.where(cb.equal(root.get(Individual_.id), id));
Individual i = em.createQuery(query)
.setMaxResults(1) // assertion fails if individual has 2 contacts
.getResultList()
.get(0);
assertEquals(2, i.getContacts().size());
So, I am using getResultList() without limit -- a bit unsatisfying.
Related
i have problem with translated query, ToList(), AsEnumerable etc.
I need construct or create query which is shared.
Branches -> Customer -> some collection -> some collection
Customer -> some collection -> some collection.
Do you help me how is the best thingh how to do it and share the query.
i access to repository via graphql use projection etc.
public IQueryable<CustomerTableGraphQL> BranchTableReportTest(DateTime actualTime, long userId)
{
var r =
(
from b in _dbContext.Branches
let t = Customers(b.Id).ToList()
select new CustomerTableGraphQL
{
Id = b.Id,
Name = b.Name,
Children =
(
from c in t
select new CustomerTableGraphQL
{
Id = c.Id,
Name = c.Name
}
)
.AsEnumerable()
}
);
return r;
}
public IQueryable<Customer> Customers(long branchId) =>
_dbContext.Customers.Where(x => x.BranchId.Value == branchId).ToList().AsQueryable();
Some example how to doit and share iquearable between query
Using ToList / AsEnumerable etc. entirely defeats the potential benefits of using IQueryable. If your code needs to do this rather than return an IQueryable<TEntity> then you should be returning IEnumerable<TResult> where TResult is whatever entity or DTO/ViewModel you want to return.
An example of an IQueryable<TEntity> repository pattern would be something like this:
public IQueryable<Customer> GetCustomersByBranch(long branchId) =>
_dbContext.Customers.Where(x => x.BranchId.Value == branchId);
Normally I wouldn't really even have a repository method for that, I'd just use:
public IQueryable<Customer> GetCustomers() =>
_dbContext.Customers.AsQueryable();
... as the "per branch" is simple enough for the consumer to request without adding methods for every possible filter criteria. The AsQueryable in this case is only needed because I want to ensure the result matches the IQueryable type casting. When your expression has a Where clause then this is automatically interpreted as being an IQueryable result.
So a caller calling the Repository's "GetCustomers()" method would look like:
// get customer details for our branch.
var customers = _Repository.GetCustomers()
.Where(x => x.BranchId == branchId)
.OrderBy(x => x.LastName)
.ThenBy(x => x.FirstName)
.Select(x => new CustomerSummaryViewModel
{
CustomerId = x.Id,
FirstName = x.FirstName,
LastName = x.LastName,
// ...
}).Skip(pageNumber * pageSize)
.Take(pageSize)
.ToList();
In this example the repository exposes a base query to fetch data, but without executing/materializing anything. The consumer of that call is then free to:
Filter the data by branch,
Sort the data,
Project the data down to a desired view model
Paginate the results
... before the query is actually run. This pulls just that page of data needed to populate the VM after filters and sorts as part of the query. That Repository method can serve many different calls without needing parameters, code, or dedicated methods to do all of that.
Repositories returning IQueryable that just expose DbSets aren't really that useful. The only purpose they might provide is making unit testing a bit easier as Mocking the repository is simpler than mocking a DbContext & DbSets. Where the Repository pattern does start to help is in enforcing standardized rules/filters on data. Examples like soft delete flags or multi-tenant systems where rows might belong to different clients so a user should only ever search/pull across one tenant's data. This also extends to details like authorization checks before data is returned. Some of this can be managed by things like global query filters but wherever there are common rules to enforce about what data is able to be retrieved, the Repository can serve as a boundary to ensure those rules are applied consistently. For example with a soft-delete check:
public IQueryable<Customer> GetCustomers(bool includeInactive = false)
{
var query = _context.Customers.AsQueryable();
if (!includeInactive)
query = query.Where(x => x.IsActive);
return query;
}
A repository can be given a dependency for locating the current logged in user and retrieving their roles, tenant information, etc. then use that to ensure that:
a user is logged in.
The only data retrieved is available to that user.
An appropriate exception is raised if specific data is requested that this user should never be able to access.
An IQueryable repository does require a Unit of Work scope pattern to work efficiently within an application. IQueryable queries do no execute until something like a ToList or Single, Any, Count, etc. are called. This means that the caller of the repository ultimately needs to be managing the scope of the DbContext that the repository is using, and this sometimes rubs developers the wrong way because they feel the Repository should be a layer of abstraction between the callers (Services, Controllers, etc.) and the data access "layer". (EF) To have that abstraction means adding a lot of complexity that ultimately has to conform to the rules of EF (or even more complexity to avoid that) or significantly hamper performance. In cases where there is a clear need or benefit to tightly standardizing a common API-like approach for a Repository that all systems will conform to, then an IQueryable pattern is not recommended over a general IEnumerable typed result. The benefit of IQueryable is flexibility and performance. Consumers decide and optimize for how the data coming from the Repository is consumed. This flexibility extends to cover both synchronous and asynchronous use cases.
EF Core will translate only inlined query code. This query will work:
public IQueryable<CustomerTableGraphQL> BranchTableReportTest(DateTime actualTime, long userId)
{
var r =
(
from b in _dbContext.Branches
select new CustomerTableGraphQL
{
Id = b.Id,
Name = b.Name,
Children =
(
from c in _dbContext.Customers
where c.BranchId == b.Id
select new CustomerTableGraphQL
{
Id = c.Id,
Name = c.Name
}
)
.AsEnumerable()
}
);
return r;
}
If you plan to reuse query parts, you have to deal with LINQKit and its ExpandableAttribute (will show sample on request)
i am trying to reuse a dynamic query as a named query as described here:
https://wiki.eclipse.org/EclipseLink/Release/2.5/JPA21#Add_Named_Query
the goal is to build the criteria-query only once and then reuse it as a namedquery if parameter did not change.
public static List<User>getUserByParameter(ParameterMap parameter){
EntityManager em = getEntityManager();
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<User> criteriaQuery = builder.createQuery(User.class);
Root<User> user = criteriaQuery.from(User.class);
List<Predicate> predicates = new ArrayList<Predicate>();
//...build up the query depending on parameter
if (null != parameter.getStatus()){
predicates.add(builder.equal(user.<Integer>get("status"), parameter.getStatus()));
}
//etc.
criteriaQuery.select(user).where(predicates.toArray(new Predicate[]{}));
Query query = em.createQuery(criteriaQuery);
//now register this query as a namedQuery
em.getEntityManagerFactory().addNamedQuery("userByParameter", query);
return query.getResultList();
}
i thought about something like:
public static List<User>getUserByParameter(ParameterMap parameter){
Query userByParameter = em.createNamedQuery("userByParameter");
if (null != userByParameter){
return userByParameter.getResultList();
}else {
//build the dynamic query as above
}
}
this results in a nullpointer as the namedQuery doesn't exist the first time.
how can i reuse the query in the same method or in other words, how can i check in a clean way (without using try-catch) if a namedquery exists?
I'm not sure I understand the problem you are looking to solve. The getUserByParameter method is something that should be built on the EntityManagerFactory, when it is first initialized or obtained. Feel free to add properties to your factory if you wish to keep track of what you have added already, but these should be done only once, upfront during initialization.
What is confusing is that you are expecting the query results to be reused - named queries are designed to help reduce the cost of parsing and preparing queries. EclipseLink has a query cache feature that can return the results for you if the same parameters are used, without you needing to cache the query, its parameters and the results yourself.
when joining I get one select per row. Solution is batch fetch but I dont want that annotation everywhere...
http://eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_batchfetch.htm
Why do I even need this? One select per row is awful... How can I set this globally? Cheers
Maybe not the ideal solution, but you may try to use JPA hints along with Java generics:
public <T> TypedQuery<T>
createBatchQuery(String ql, Class<T> clazz, String type, String size, String relation) {
return em.createQuery(jpql, clazz)
.setHint(QueryHints.BATCH_TYPE, type)
.setHint(QueryHints.BATCH_SIZE, size)
.setHint(QueryHints.BATCH, relation);
}
The above query may then be used globally and extended with concrete query implementations according to you needs, i.e.
String jpql = "SELECT c FROM Country c WHERE c.name = :name"; // or #NamedQuery
TypedQuery<Country> q = createBatchQuery(jpql, Country.class, "JOIN", "64", "c.cities");
q.setParameter("name", "Australia");
Country c = q.getSingleResult();
Articles on this topic:
Batch fetching - optimizing object graph loading
EclipseLink/Examples/JPA/QueryOptimization
I always get the same error when using the criteria API:
Local Exception Stack:
Exception [EclipseLink-6075] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.QueryException
Exception Description: Object comparisons can only use the equal() or notEqual() operators. Other comparisons must be done through query keys or direct attribute level comparisons.
Expression: [
Relation operator LIKE
Query Key rootId
Base de.uni.entities.Diary
Constant Testaccount]
Query: ReportQuery(referenceClass=Diary )
The code with the criteria API is the following:
....
CriteriaBuilder cb = getEm().getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
// Restrictions
Predicate[] predicate = new Predicate[1];
// From-clause
Root<User> root2R = cq.from(User.class);
predicate[0] = cb.like(root2R.<String> get("rootId"), id);
Join<Clazz, Diary> friends2R;
friends2R = root2R.join("diaries");
cq.where(predicate);
// Select-clause
cq.select((Selection<? extends Clazz>) friends2R);
Query query = getEm().createQuery(cq);
List<Object> data = query.getResultList();
return data;
}
The problem exists because of the following two lines:
" Join friends2R;
friends2R = root2R.join("diaries");"
Without the mapping (with an adjusted select-clause) i would get the searched User with the fitting rootId, so that there occurs no error. But now i wanna map the User with the Diaries and show all diaries of the User-entity. But it always ends in the error ahead. Whether i use "like" or "equal", it doesn't work.
The generell code shouldn't have an error, because i'm using the same code already to get all mapped Users of a User (many to many relation) => no problem.
I just don't understand, why this error occurs and especially why it mentions the Diary as Base although the user should be mentioned as the base-class...and the id is right too and also appears so in the database...
I really hope that you can help me, thanks in advance!
How is rootId mapped, using a Basic, or OneToOne?
Seems to be a bug, please log the bug on EclipseLink.
Your code does not seem correct though, the
CriteriaQuery cq = cb.createQuery(User.class);
should be,
CriteriaQuery cq = cb.createQuery(Diary.class);
should it not?
Doesn't Diary also have a relationship to User? You could just query Diary where its user has the id.
I'm having problems querying the entity model to get additional information.
My db has a Program table with a one to many relation with an Events table. The Entity model generates the relationships just fine, but I'm unable to figure out how to query the model to get the progam object with its events.
I can do this:
var foo = from program in entities.ProgramSet
where program.StartDate > DateTime.now
orderby program.StartDate
select program;
No problems there. From what I've found on Microsofts Page (Shaping queries with Entity framework): msdn.microsoft.com/en-us/library/bb896272.aspx, if I wanted to get the child objects, I just do the following:
// Define a LINQ query with a path that returns
// orders and items for a contact.
var contacts = (from contact in context.Contact
.Include("SalesOrderHeader.SalesOrderDetail")
select contact).FirstOrDefault();
However, there is no .Include or Include that I can find on the query.
Any suggestion? I know that I can do a foreach across the results, then run a .Events.Load() on it, but doesn't that force the IQueriable result to execute the sql, instead of optomize it to run only when a .ToList() etc is called on it?
Here is some sample code from my project:
public class ProgramRepository : CT.Models.IProgramRepository
{
CTEntities db = new CTEntities();
public IQueryable<Program> FindAllPrograms()
{
return db.ProgramSet;
}
public IQueryable<Program> FindUpcomingPrograms()
{
var programs = from program in FindAllPrograms()
where program.StartDate > DateTime.Now
orderby program.StartDate
select program;
return programs;
}
With the FindUpComingPrograms I would like to have it also include the Events Data. There is a relationship between the Program and Events model. Program has a List<Events> property, that I would like to fill and return with the IQueryable method.
Thanks again!
The Include Function is part of the ObjectQuery object...
I think you are going to need to re-write your query to look something like this:
var contacts = context.Contact.Include("SalesOrderHeader.SalesOrderDetail").FirstOrDefault();
//Not sure on your dot path you might have to debug that a bit
Here is an Article that has some examples...