I joined two tables and get different columns from both tables to load them into dataTable. I want to order the columns by using dataTable but how can I understand which columns belogns to which tables? I mean, when I click order button on a last_name column from datatable, how can I write order by table1_.last_name desc because there are columns from table2 though.
So my code looks like this;
public Page<UserDTO> findByCriteria(String columnName, final String filters, String sort, Pageable pageable) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<UserDTO> cq = cb.createQuery(UserDTO.class);
Root<UserDTO> iRoot = cq.from(UserDTO.class);
Join<UserDTO, User> bJoin= iRoot.join("user");
cq.multiselect(bJoin.get("id"), bJoin.get("login"), bJoin.get("firstName"), bJoin.get("lastName"), bJoin.get("dayOfBirth"), iRoot.get("district"));
........
if (!columnName.equals("district")) {
.....
}
.......
if (sort.split(":")[0].equals("district") || columnName.equals("district")) {
.......
}
.........
}
As you see, I've used !columnName.equals("district") to distinguish tables from each other but this is not a generic way. I cannot use this method for another tables because they may use different columns so I'll have to change the columnName to differentitate tables again and again.
What I'm asking is that, is there a method like contains for iRoot and bJoin to check if table contains that columnName ?
In Criteria API you should think in terms of entities and their properties, not in terms of tables and columns.
So, assuming you want to order on (for instance) UserDTO.lastName, this will be probably something along the lines:
cq.orderBy(iRoot.get("lastName"));
What I'm asking is that, is there a method like contains for iRoot and bJoin to check if table contains that columnName ?
Root has a method getModel which returns an EntityType which provides metadata on the entity. You can check for attributes etc. there. Not exactly column names, but close.
I solved my problem by using try catch:
try {
bJoin.<String>get(sort.split(":")[0]).as(String.class);
//Use bJoin table to order
}catch(Exception e) {
//Use iRoot table to order
}
By implementing try catch, I aimed to make a generic way to distinguish tables from each other.
Related
I am writing an application with Spring 4 and Hibernate 5.1. I need to be able to interoperate with a legacy system that saves SQL queries using the native table and column names. I need to be able to add a Selection object to my Tuple query which uses the original column name rather then the entity field name.
I tried doing this using Hibernate with Projections.sqlProjection(column_name... and that sort of worked, but other issues are preventing me from continuing in this direction.
#Transactional(propagation = Propagation.MANDATORY)
public List<Object[]> testQuery() {
Class<?> rootClass = getEntityClassByTable("pm_project");
List<String> columnList = new ArrayList<>();
columnList.add("pm_project.pm_project_id");
columnList.add("pm_project.pm_project_name");
columnList.add("pm_project.pm_project_title");
columnList.add("pm_project.parent_id");
columnList.add("pm_project.pm_project_from_date");
CriteriaQuery<Tuple> query = criteriaBuilder.createTupleQuery();
Root<?> root = query.from(rootClass);
List<Selection> selectionList = new ArrayList<>();
for (String column : columnList) {
Selection s = nativeSelection(column);
selectionList.add(s);
}
query.multiselect(selectionList.toArray());
List<Tuple> resultList = em.createQuery(query).getResultList();
return resultList;
}
I need 'NativeSelection', something that produces a Selection for a native column name, not an entity attribute name. It would be really good if it took any sql that could be a field, because some JSON_VALUE fields might pop up.
The values of the native sql are dynamic, and I am not receiving a full SQL, only column and table names, with possible JSON_VALUE calls. I would very much like not to have to generate native sql, as I want to leverage other JPA features in my query.
If you are using native query then no need to specify entity attribute names. All you have to do is to set the value of the nativeQuery attribute to true and define the native SQL query in the value attribute of the annotation
#Query(
value = "SELECT * FROM pm_project p WHERE p.pm_project_id= 1",
nativeQuery = true)
Collection<Project> findProjectById();
You can also do it without using native query by simple JPA
List<Project> findAllByProjectId(Integer id);
I am using paging and sorting repository for jquery datatable in my spring project. I tried searching with three parameters as of now like
#Query(value = "Select p from ProfileBaseInfo p where p.fullName = :fullName or p.firstName = :firstName or p.lastName = :lastName")
Page<ProfileBaseInfo> search(#Param("fullName") String fullName,#Param("firstName") String firstName,#Param("lastName") String lastName,Pageable pageable);
Now i need to search 5 more parameters additionally with different combinations(i.e. and/or). I have generated a dynamic query based on the parameters searched. (i.e)If a parameter is present, I will join the table and insert the where condition. How do I bring the dynamically generated query inside of #Query or should I handle this in a different way? Now i what i want is I have to build a criteria like "SELECT * FROM profile where name=? and fullName=? and title=? and city=? and state=? and country=?" in ***#Query*** Annotation.
I would like to know if there is another way to do because the number of columns in a table can be high.
I think you should create a Spring bean with implementation of your search method (Spring Data).
#Autowire some bean to talk with DB (EntityManager or similar) and write down whatever query you need.
I'd recommend you to use some kind of wrapper object for all your parameters, like
class Parameters {
String firstName,
String fullName;
...
Object paramN;
<<getters-setters ?>>
}
and to use CriteriaQuery to construct query according to parameters, like
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<ProfileBaseInfo> cq = cb.createQuery(ProfileBaseInfo.class);
Root<ProfileBaseInfo> from = cq.from(ProfileBaseInfo.class);
...
List<Predicate> predicates = Lists.newArrayList();
...
if (param.getFullName() != null) {
predicates.add(cb.like(cb.lower(ProfileBaseInfo_.fullName)), param.getFullName().toLowerCase() + "%"))
}
...
cb.and(predicates.toArray(new Predicate[predicates.size()]));
So you can expand parameters list accroding to needs and there is no need to work with strings.
I cannot find a solution to a problem that seems to be easy. Say there are 2 entity classes:
class A {
Set<B> bs;
}
class B {
String text;
}
How to create a criteria query that returns all A's that contains at least one B entity which fulfills a given condition (like b.text = 'condition')?
I think this link can be useful:
http://mikedesjardins.net/2008/09/22/hibernate-criteria-subqueries-exists/
It contains the following example about how create n exists criteria:
"What you’re really trying to do is to obtain all Pizza Orders where an associated small pizza exists. In other words, the SQL query that you’re trying to emulate is
SELECT *
FROM PIZZA_ORDER
WHERE EXISTS (SELECT 1
FROM PIZZA
WHERE PIZZA.pizza_size_id = 1
AND PIZZA.pizza_order_id = PIZZA_ORDER.pizza_order_id)
The way that you do that is by using an “exists” Subquery, like this:
Criteria criteria = Criteria.forClass(PizzaOrder.class,"pizzaOrder");
DetachedCriteria sizeCriteria = DetachedCriteria.forClass(Pizza.class,"pizza");
sizeCriteria.add("pizza_size_id",1);
sizeCriteria.add(Property.forName("pizza.pizza_order_id").eqProperty("pizzaOrder.pizza_order_id"));
criteria.add(Subqueries.exists(sizeCriteria.setProjection(Projections.property("pizza.id"))));
List<pizzaOrder> ordersWithOneSmallPizza = criteria.list();
And voila, the result will contain two PizzaOrders!"
I have a table "mytable" with a column "name". I wish to query for all rows that start with a certain string or have the string somewhere in the name, and order them in a way that the rows that start with the name come first. Is it possible to have just one query?
I cannot use "union", I'm using Hibernate and it is not supported. I would prefer not to use views as they are not cached by Hibernate second level cache.
perhaps something like this could work?
select nvl(mytable1.name, '') || nvl(mytable2.name, '') as name,
nvl(mytable1.i, '') || nvl(mytable2.i, '') as i from
(select 1 as i, name from mytable where name like ('string%')) mytable1
full outer join
(select 2 as i, name from mytable where name like ('%string%')) mytable2
where (mytable1.name is null or mytable2.name is null)
and mytable1.name != mytable2.name
order by i, name
but I think I would have used one of the following:
a simple query with union for this query
a sort in Java instead, and do it as 2 queries.
In Oracle you can do:
select *
from myTable mt
where mt.column like '%TargetString%'
order by instr(mt.column, 'TargetString')
If you are using Hibernate + Oracle, it supports instr function in the dialect.
I believe other RDBMSs has similar functions too, which may be supported by Hibernate dialect.
Edit: Since you have an aversion to leveraging more than one Criteria Query - for whatever reason - I would suggest reading up on the Order class. It will not provide you with what you want. That leaves you with few options.
One option would be to leverage Hibernate Search.
Another would be to leverage Hibernate for the query and then Collections.sort(...) for your ordering.
List<MyObject> lstMyObject = ...;
Collections.sort(lstMyObject, new Comparator<MyObject>() {
#Override
public int compare(MyObject o1, MyObject o2) {
// Assuming 'argument' is passed to this method and defined as 'final'
return o1.getName().startsWith(argument) && o2.getName().startsWith(argument) ? 0 :
o1.getName().startsWith(argument) ? -1 : 1;
}
}
This satisfies your requirement of 1) Using Hibernate, 2) Not using multiple queries, 3) Not using native SQL, 4) Not using views, 5) Not using a 'union'.
My advice would be two queries, something like:
public List<MyObject> findMyObjects(...) {
Criteria queryStartsWith = sf.getCurrentSession().createCriteria(MyObject.class)
.add(Restrictions.like("myProperty",nameArgument,MatchMode.START))
.addOrder(Order.desc);
Criteria queryLike = sf.getCurrentSession().createCriteria(MyObject.class)
.add(Restrictions.like("myProperty",nameArgument,MatchMode.ANYWHERE))
.addOrder(Order.desc);
List<MyObject> lstMyObject = new ArrayList<MyObject>();
for (Object curObject : queryStartWith.list())
if (curObject instanceOf MyObject)
lstMyObject.add((MyObject) curObject);
for (Object curObject : queryLike.list())
if (curObject instanceOf MyObject)
lstMyObject.add((MyObject) curObject);
return lstMyObject;
}
I'm not sure Hibernate will accommodate your conditional Ordering. The org.hibernate.criterion.Order class is pretty limited, to be honest.
Other than using native SQL, this is the only option I can think of off the top of my head.
I'm trying to find information about how to select only certain fields of an entity using Spring Data (I'm using JPA). I want to select only specific information of an entity, the repository interfaces gives you the ways to return the information of the WHOLE entity!. Some times I only need 2 or 3 fields of an entity and returning 20,30, ...100.. fields may be a little overkill.
This kind of functionality is something that I would do using Hibernate Criteria Projections, or even JPA "SELECT NEW ...." queries. Don't know if it is possible with Spring Data.
Thanks.
What you can do is return a List<Object[]> from repository. Then in your service class iterate over this list and manually create the object you need. Sample repository method
#Query("select el.moduleId, el.threadId from ExceptionLog el")
public List<Object[]> tempQuery();
I think you can also do it in this way
SomeDataPOJO{
required col1
required col2
}
and then write query like this
#Query("select new SomeDataPOJO from requiredTable where xyz="abc")
public List<SomeDataPoJO> tempQuery()
Its not plain Spring Data but did you consider using Springs JdbcTemplate? Its also in the Context if you use Spring Boots Autoconfiguration and has several handlers for transforming results of the Query.
For Example for the Query SELECT a, b FROM EMPLOYEE WHERE ID = ? you could use
String query = "SELECT a, b FROM EMPLOYEE WHERE ID = ?";
List<Pair<String,Integer>> employees = jdbcTemplate.queryForObject(
query, new Object[] { id }, new ExampleRowMapper());
Where the ExampleRowMapper transforms each row from the result into your given Return type (Pair<String, Integer> in this case) and could look like
public class ExampleRowMapper implements RowMapper<Pair<String, Integer>> {
#Override
public Pair<String, Integer> mapRow(ResultSet rs, int rowNum) throws SQLException {
return Pair.of(rs.getString(1), rs.getString(2));
}
}
Example adapted from https://www.baeldung.com/spring-jdbc-jdbctemplate where you find more information.
Of course its not as typesafe as a JPQL as the Query is "raw" or "native" SQL but at least the response is again typesafe and I like it more than to return Object[] or something.