I'm selecting records in JAVA with JPA and playframework like this:
EntityManager em = JPA.em();
List<News> resultUrl = News.find("link", url).fetch();
if (resultUrl.isEmpty()) { //check if it is exist
}
But i want to select records with two condition, like this:
where link='url' and name='joe'
How can i do this?
Thanks for helping.
Best wishes.
Use:
Query q = em.createQuery("FROM News n WHERE n.link=:url and n.name=:name");
q.setParameter("url", "url").setParameter("name", "joe");
List<News> resultUrl = q.getResultList();
...
One way to do it with Play is
List<News> resultUrl = News.find("byLinkAndName", url, "joe").fetch();
if (resultUrl.isEmpty()) { //check if it is exist
}
Another:
List<News> resultUrl = News.find("link = ? and name = ?", url, "joe").fetch();
if (resultUrl.isEmpty()) { //check if it is exist
}
My proposal is to define a named query:
#Entity
#NamedQueries({
#NamedQuery(name = News.FIND_BY_URL_AND_NAME, query = "Select n FROM News as n WHERE n.url=:" + News.PARAM_URL + " AND n.name=:" + News.PARAM_NAME)
})
public class News {
public static final String FIND_BY_URL_AND_NAME = "News.findByUrlAndName";
public static final String PARAM_URL = "url";
public static final String PARAM_NAME = "name";
//CONTINUE
}
Then you call it like that:
Query query = em.createNamedQuery(News.FIND_BY_URL_AND_NAME);
query.setParameter(News.PARAM_URL, "url");
query.setParameter(News.PARAM_NAME, "name");
List<News> news = query.getResultList();
Get a look at CriteriaBuilder, CriteriaQuery and Predicate :
EntityManager em = JPA.em();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<T> criteriaQuery = cb.createQuery(News.class);
Root<T> root = criteriaQuery.from(News.class);
criteriaQuery.select(root);
List<Predicate> ps = new ArrayList<Predicate>();
ps.add(sb.equal(root.get("link", url));
ps.add(sb.equal(root.get("name", "joe"));
criteriaQuery.where(cb.and(ps.toArray(new Predicate[0])));
List<News> resultUrl = em.createQuery(criteriaQuery).getResultList();
Regards
Related
I have 4 tables in DB, such as
Product => PK productid
Variant => PK variantid FK productid
Images => PK imageid FK variantid
Attribute => PK attributid FK variantid
So need to perform following action in Java Rest API
Pagination with sorting and filtering
One Product Object has different Variant with there images and attributes.
Below is the #Repository code for single table pagination i.e Product
#Repository
public class PaginProductCriteriaRepository {
private final EntityManager entityManager;
private final CriteriaBuilder criteriaBuilder;
public PaginProductCriteriaRepository(EntityManager entityManager) {
this.entityManager = entityManager;
this.criteriaBuilder = entityManager.getCriteriaBuilder();
}
public Page<Product> findAllWithFilter(PaginProductPage paginPage, PaginProductSearchCriteria paginSearchCriteria){
CriteriaQuery<Product> productCriteriaQuery = criteriaBuilder.createQuery(Product.class);
Root<Product> rootProduct = productCriteriaQuery.from(Product.class);
Predicate predicate = getPrediate(paginSearchCriteria,rootProduct);
productCriteriaQuery.where(predicate);
setOrder(paginPage,productCriteriaQuery,rootProduct);
TypedQuery<Product> typedQuery = entityManager.createQuery(productCriteriaQuery);
typedQuery.setFirstResult(paginPage.getPageNumber() * paginPage.getPageSize());
typedQuery.setMaxResults(paginPage.getPageSize());
Pageable pageable = getPageable(paginPage);
long paginCount = getPaginCountMethod(predicate);
return new PageImpl<>(typedQuery.getResultList(),pageable,paginCount);
}
private Predicate getPrediate(PaginProductSearchCriteria paginSearchCriteria, Root<Product> root) {
List<Predicate> predicates = new ArrayList<>();
if (Objects.nonNull(paginSearchCriteria.getpName())) {
predicates.add(criteriaBuilder.like(root.get("pName"),"%" + paginSearchCriteria.getpName() + "%"));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
}
private void setOrder(PaginProductPage paginPage, CriteriaQuery<Product> criteriaQuery, Root<Product> root) {
if (paginPage.getSortDirection().equals(Sort.Direction.ASC)) {
criteriaQuery.orderBy(criteriaBuilder.asc(root.get(paginPage.getSortBy())));
} else {
criteriaQuery.orderBy(criteriaBuilder.desc(root.get(paginPage.getSortBy())));
}
}
private Pageable getPageable(PaginProductPage paginPage) {
Sort sort = Sort.by(paginPage.getSortDirection(),paginPage.getSortBy());
return PageRequest.of(paginPage.getPageNumber(),paginPage.getPageSize(),sort);
}
private long getPaginCountMethod(Predicate predicate) {
CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
Root<Product> countRoot = countQuery.from(Product.class);
countQuery.select(criteriaBuilder.count(countRoot)).where(predicate);
return entityManager.createQuery(countQuery).getSingleResult();
}
}
Expectation:
Using above code pagination is working fine using single Table i.e Product BUT I need to join multiple tables with product and get full product object contain variants, images and attributes.
Note: Single Product has multiple variants, variant has muluple attribute and images
My system required to add filters,and I'm wonder if there any query that like this
SELECT *
FROM posts p
when byDate is not null then (where p.createAt BETWEEN :startDate AND :endDate)
when byType is not null then (where p.type = :type)
I knew that the query is not valid, but I want at one query to get the data wherever the request has (no filter or all filters or some of filters).
My goal is to create one query to achieve all cases.
It's usually not a good idea to write a big SQL query when you can tell in advance the actual query you want to run.
If you want to run a different query based on conditions you know before running the query, there are different approaches in JPA or Spring that you can use
Spring
You can define the different queries using Spring Data query methods?
public class PostRepository implements JpaRepository<Post, Long> {
List<Post> findByCreatedAtBetween(Date startDate, Date endDate);
List<Post> findByTypeIs(String type);
}
And then somewhere in the code, you can:
List<Post> results = null;
if (byDate != null) {
results = repository.findByCreatedAtBetween(startDate, endDate);
} else if (byType != null) {
results = repository.findByTypeIs(type);
} else {
results = repository.findAll();
}
Criteria
With criteria you can create a dynamic query at runtime and execute it:
public class PostRepository implements PostRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Override
public List<Post> findPosts(Filter filter) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(Post.class);
Root<User> user = query.from(Post.class);
if ((filter.getByDate() != null)) {
// byDate is not null
ParameterExpression<Date> startDate = builder.parameter( Date.class );
ParameterExpression<Date> endDate = builder.parameter( Date.class );
query.where(builder.between( b.get( "createdAt" ), startDate, endDate));
return em.createQuery(query)
.setParameter(startDate, ...)
.setParameter(endDate, ...)
.getResultList();
}
if (filter.getByType() != null) {
ParameterExpression<Date> typeParam = builder.parameter( Date.class );
query.where(builder.and(root.get("type"), typeParam));
return em.createQuery(query)
.setParameter(typeParam, ...)
.getResultList();
}
return entityManager.createQuery(query)
.getResultList();
}
}
Assuming that your entity has the fields type and createdAt.
This approach works well if you don't know in advance what's your query looks like. For example, when you don't know how many conditions you will have to add to it.
But, if I know already which query I want to run, then I prefer to use HQL/JPQL.
HQL
If your queries don't change and you already know what they look like,
I find it easier to define them with HQL:
public class PostRepository implements PostRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Override
public List<Post> findPosts(Filter filter) {
if (filter.getByDate() != null) {
return em.createQuery("from Post p where p.createdAt between :startDate and :endDate", Post.class)
.setParameter("startDate", ...)
.setParameter("endDate", ...)
.getResultList();
}
if (filter.getByType() != null) {
return em.createQuery("from Post p where p.type =:type", Post.class)
.setParameter("type", ...)
.getResultList();
}
return em.createQuery("from Post", Post.class)
.getResultList();
}
}
You can refactor the code to make it more elegant, but it should give you an idea. Note that if you need to reuse the same queries in different services, it might be helpful to define them using the annotation #NamedQuery.
Filters
In Hibernate (not JPA) you can also define filters. They are SQL filter conditions that one can apply at runtime:
#Entity
#FilterDef(name = Post.BY_DATE, defaultCondition = "createdAt between :startDate and :endDate", parameters = {#ParamDef(name = "startDate", type = "date"), #ParamDef(name = "startDate", type = "date") })
#FilterDef(name = Post.BY_TYPE, defaultCondition = "type = :type", parameters = #ParamDef(name = "startDate", type = "date"))
#Filter(name = Post.BY_DATE)
#Filter(name = Post.BY_TYPE)
class Post {
static final String BY_DATE = "Post.byDateFilter";
static final String BY_TYPE = "Post.byFilter"
private String type;
private Date createdAt;
...
}
Then:
public class PostRepository implements PostRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Override
public List<Post> findPosts(Filter filter) {
enableFilters(em);
return em.createQuery("from Post", Post.class).getResultList();
}
private void enableFilters(Filter filter, EntityManager em) {
if (filter.getByDate() != null) {
em.unwrap(Session.class)
.enableFilter( Post.BY_DATE )
.setParameter("startDate", ...)
.setParameter("endDate", ...);
} else if (filter.getByType() != null) {
em.unwrap(Session.class)
.enableFilter( Post.BY_TYPE )
.setParameter("type", ...);
}
}
}
I got this exception,
org.hibernate.QueryException: expecting 'd', found 'f' [from
com.carre.model.ProductOrder where sessieid =
4f5bf46709a6886e764207a00ec0]
code form controller:
Cookie[] cookies = request.getCookies();
String sessieid = cookies[0].getValue();
model.addAttribute("listProductorderNieuw", this.productorderService.listProductorderNieuw(sessieid));
code form DAO:
#SuppressWarnings("unchecked")
#Override
public List<ProductOrder> listProductorderNieuw(String id) {
Session session = this.sessionFactory.getCurrentSession();
List<ProductOrder> productList = session.createQuery("from ProductOrder where sessieid = " + id).list();
for (ProductOrder p : productList) {
logger.info("ProductOrder List::" + p);
}
return productList;
}
I have no idea where this exception is coming from.
Someone can help?
Can you try using named paramner as follows
Query query = session.createQuery("from ProductOrder where sessieid = :id ");
query.setParameter("id", id);
List<ProductOrder> productList = query.list();
Im having a trouble with my code. i have a view jsp where i can view all items.
When i try to search for a name. It will loop or have a redundancy. I dont know why. looks like this.
BTW i have two tables and a foreign key product[pid] to stock[pid]
public class Product {
#Id
#Column(name="p_id")
private String pid;
#Column(name="p_name")
private String p_name;
#Column(name="c_name")
private String c_name;
#Column(name="b_name")
private String b_name;
//SETTERS & GETTERS
public class Stock {
#Id
#Column(name="s_id")
private int sid;
#Column(name="p_id")
private String pid;
#Column(name="s_quantity")
private String squantity;
#Column(name="s_price")
private String sprice;
#Column(name="s_cost")
private String cost;
//SETTERS AND GETTERS
#Controller
#RequestMapping(method = RequestMethod.POST, value = "/searchItem")
public String searchItem(HttpServletRequest request, ModelMap map,
#RequestParam(value = "page", required = false) Integer page,
#RequestParam(value = "size", required = false) Integer size ) {
String searchProductName = request.getParameter("productName");
String cat = request.getParameter("category");
String bran = request.getParameter("brand");
Product searchProduct = new Product();
searchProduct.setP_name(searchProductName);
searchProduct.setC_name(cat);
searchProduct.setB_name(bran);
int pageSize = (size != null && size != 0) ? size : 25;
int firstResultIndex = (page != null && page > 0) ? (page - 1) * pageSize : 0;
List<Product> productList = catService.getUsers(searchProduct, firstResultIndex, pageSize);
map.addAttribute("productList", productList);
List<Category> cList = catService.getCat();
map.addAttribute("cList", cList);
List<Brand> bList = catService.getBrand();
map.addAttribute("bList", bList);
return "new/list";
}
#DaoImpl
#SuppressWarnings("unchecked")
#Override
public List<Product> getUsers(Product searchProduct, int startPage, int maxResults) {
EntityManager entityManager = transactionManager.getEntityManagerFactory().createEntityManager();
Session session = entityManager.unwrap(Session.class);
SQLQuery query = session.createSQLQuery("FROM product,stock");
boolean paramExists = false;
if (!StringUtility.isStringNullOrEmpty(searchProduct.getC_name())&&!StringUtility.isStringNullOrEmpty(searchProduct.getB_name())) {
//sqlQuerySB.append(" product.c_name LIKE :category AND product.b_name LIKE :brand");
query = session.createSQLQuery("FROM product,stock WHERE product.c_name LIKE :category AND product.b_name LIKE :brand");
paramExists = true;
}
if (!StringUtility.isStringNullOrEmpty(searchProduct.getP_name())) {
query = session.createSQLQuery("SELECT product.p_name,product.c_name,product.b_name,stock.s_quantity,stock.s_price,stock.s_cost FROM product,stock WHERE product.p_name LIKE :productName");
query.setParameter("productName", "%" + searchProduct.getP_name() + "%");
paramExists = true;
}
if (!StringUtility.isStringNullOrEmpty(searchProduct.getC_name())) {
query = session.createSQLQuery("SELECT product.p_name,product.c_name,product.b_name,stock.s_quantity,stock.s_price,stock.s_cost FROM product,stock WHERE product.c_name LIKE :category ");
query.setParameter("category", "" + searchProduct.getC_name() + "");
paramExists = true;
}
query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
List<Product> productList = query.list();
if (entityManager.isOpen())
entityManager.close();
return productList;
}
maybe there is a big problem in my DAOimpl on how i query two tables..
need some help/advice. ty.
You are not specifying the join criteria between product and stock:
SELECT
product.p_name,
product.c_name,
product.b_name,
stock.s_quantity,
stock.s_price,
stock.s_cost
FROM product, stock
WHERE product.p_name LIKE :productName
In this case it will return one row for each for each combination of product (with the name) and stock (full table since it does not have any criteria).
Try to specify the join criteria:
SELECT
product.p_name,
product.c_name,
product.b_name,
stock.s_quantity,
stock.s_price,
stock.s_cost
FROM product, stock
WHERE
product.pid = stock.pid
product.p_name LIKE :productName
Given the query example here: http://www.hibernatespatial.org/tutorial-hs4.html
Query query = em.createQuery("select e from Event e where within(e.location, :filter) = true", Event.class);
query.setParameter("filter", filter);
Is it possible to rewrite the query using jpa 2 criteria api?( I am unsure how i should deal with the within(e.location, :filter) part.
I recently work at the exact same problem. My solution is a own Predicate for the within-keyword.
public class WithinPredicate extends AbstractSimplePredicate implements Serializable {
private final Expression<Point> matchExpression;
private final Expression<Geometry> area;
public WithinPredicate(CriteriaBuilderImpl criteriaBuilder, Expression<Point> matchExpression, Geometry area) {
this(criteriaBuilder, matchExpression, new LiteralExpression<Geometry>(criteriaBuilder, area));
}
public WithinPredicate(CriteriaBuilderImpl criteriaBuilder, Expression<Point> matchExpression, Expression<Geometry> area) {
super(criteriaBuilder);
this.matchExpression = matchExpression;
this.area = area;
}
public Expression<Point> getMatchExpression() {
return matchExpression;
}
public Expression<Geometry> getArea() {
return area;
}
public void registerParameters(ParameterRegistry registry) {
// Nothing to register
}
#Override
public String render(boolean isNegated, RenderingContext renderingContext) {
StringBuilder buffer = new StringBuilder();
buffer.append(" within(")
.append(((Renderable) getMatchExpression()).render(renderingContext))
.append(", ")
.append(((Renderable) getArea()).render(renderingContext))
.append(") = true ");
return buffer.toString();
}
}
Your query would look like this:
public List<Event> findEventInArea(Geometry area){
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Event> c = cb.createQuery(Event.class);
Root<Event> event = c.from(Event.class);
c.where(new WithinPredicate((CriteriaBuilderImpl) cb, event.get(Event_.location), area));
Query query = entityManager.createQuery(c);
return query.getResultList();
}
JPA does not support spatial. However, you can unwrap the hibernate session from your JPA EntityManager and run spatial criteria.
The lat lon bounds in this code sample is arbitrary.
#PersistenceContext(unitName = "myPuName")
private EntityManager entityManager;
#Override
public List<City> findCities() {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
Session session = entityManager.unwrap(Session.class);
Criteria criteria = session.createCriteria(City.class);
GeometryFactory geometryFactory = new GeometryFactory();
Coordinate[] coordinates = {new Coordinate(-9,-9,0),new Coordinate(-9,9,0),new Coordinate(9,9,0),new Coordinate(9,-9,0),new Coordinate(-9,-9,0)};
LinearRing polygon = geometryFactory.createLinearRing(coordinates);
Polygon po = geometryFactory.createPolygon(polygon,null);
criteria.add(SpatialRestrictions.within(City_.location.getName(), po));
List list = criteria.list();
return list;
}
Here is some more code not directly related to the question. This class can be used as an "Order" criteria to be added to a hibernate criteria. It will sort results by distance from the argument location:
public class KnnOrder extends Order {
private final Point fromPoint;
public KnnOrder(String propertyName, boolean ascending, Point fromPoint) {
super(propertyName, ascending);
this.fromPoint = fromPoint;
}
#Override
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) {
Dialect dialect = criteriaQuery.getFactory().getDialect();
if (!dialect.getClass().isAssignableFrom(PostgisDialect.class)) {
throw new UnsupportedOperationException("This supports only postgis dialect. Was requested: " + dialect.toString());
}
// final String[] columns = criteriaQuery.getColumnsUsingProjection(criteria, super.getPropertyName());
// String fromPointWkt = WKTWriter.toPoint(fromPoint.getCoordinate());
return "location <-> st_setsrid(st_makepoint(" + fromPoint.getX() + "," + fromPoint.getY() + "),4326)";
}
}
In JPA2 you can use the function expression builder. No dedicated stuff required anymore. Works for the order expression too.
public List<Event> listThem(Geometry area) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Event> cq = cb.createQuery(Event.class);
Root<Event> root = cq.from(Event.class);
ParameterExpression<Geometry> circleParm = cb.parameter(Geometry.class);
cq.where(cb.isTrue(cb.function("st_within", Boolean.class,
root.get(Event_.location), circleParm)));
TypedQuery<Event> tq = em.createQuery(cq);
tq.setParameter(circleParm, area);
return tq.getResultList();
}
Small price: The function name is database-dependent. In PostgreSQL the within function is called st_within.