Retrieve a row from DB as a Map in Hibernate - java

Table Players:
ID | name | email | age | ...
1 | 'bob' | null | 23 | ...
This table is where instances of class Player are persisted (one row per instance, no composition etc.).
Having a Hibernate Session, how do I get the row (say with id - the PK - equal to 1) as a Java Map (key = column name, value = cell value) ?
Example usage:
Map<String,String> row = getPlayerByIdAsMap(1);

Use a query with AliasToEntityMapResultTransformer; is verbose but should works with Hibernate property definition and not with JavaBean definition (they can differ).
Map<String,Object> aliasToValueMap =
session.createCriteria(User.class)
.add(Restrictions.idEq(userID))
.setProjection(Projections.projectionList()
.add(Projections.id().as("id"))
// Add others properties
)
.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE)
.uniqueResult();
A worse approch can be write a custom ResultTransformer that introspect ClassMetadata and try to extract values...
class IntrospectClassMetadata extends BasicTransformerAdapter {
PassThroughResultTransformer rt = PassThroughResultTransformer.INSTANCE;
public Object transformTuple(Object[] tuple, String[] aliases) {
final Object o = rt.transformTuple(tuple, aliases);
ClassMetadata cm = sf.getClassMetadata(o.getClass());
List<String> pns = new ArrayList<String>(Arrays.asList(cm.getPropertyNames()));
Map<String, Object> m = new HashMap<String, Object>();
for(String pn : pns) {
m.put(pn, cm.getPropertyValue(o, pn));
}
m.put(cm.getIdentifierPropertyName(), cm.getIdentifier(o));
return m;
}
}
and use
Map<String,Object> aliasToValueMap =
session.createCriteria(User.class)
.add(Restrictions.idEq(userID))
.setResultTransformer(new IntrospectClassMetadata())
.uniqueResult();
Last chance:
Map<String,Object> map = (Map<String,Object>)s.createSQLQuery("select * from user where id = :id")
.setParameter("id",p.id)
.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE)
.uniqueResult();
but this doesn't map list,bags and other mapped object, but only raw column names and values...

You can use HQL and do a query for selecting the result as a new Map
select new Map(p.id as ID, p.name as name, p.email as email, p.age as age)
from Player p
It will return you a collection of maps, being each one of the maps a row in the query result.

You can use BeanUtils and do something like this:
User user = (User) session.get(User.class, userID);
Map map = BeanUtils.describe(user);

Related

Hibernate Criteria using FIND_IN_SET

I have a below requirement in my project:
(1) Request object to an API which is off list type (of strings).
(2) We are using Hibernate Criteria to fetch the resultSet from the mysql database.
(3) One of the columns is having Pipe delimited values.
(4) We have to fetch the rows against each of the request object's values.
Table: user_info
-----------------------------------------------------------
user_id user_name .. .. .. .. hobbies
-----------------------------------------------------------
101 johnSmith .. .. .. .. Traveling|Painting|Gardening
102 tomWatch .. .. .. .. Gardening|Reading|Fishing
103 robertPatt .. .. .. .. Dancing|Gardening|Swimming|Blogging
104 julieAdams .. .. .. .. Bicycling|Fishing|Socializing
JSON Request object:
{
searchParams : {
hobbies : [
"Gardening",
"Fishing",
"Reading"
],
..
..
..
}
}
Java Code
if(!CollectionUtils.isEmpty(searchParams.getHobbies())) {
List<String> reqHobbies = searchParameters.getHobbies();
Disjunction disjunction = Restrictions.disjunction();
for(String hobby : reqHobbies) {
Criterion criterion = Restrictions.like("hobbies", "|" + hobby + "|");
disjunction.add(criterion);
}
criteria.add(disjunction);
}
This query will not work as the starting of the value (from table) is not having "|" symbol..
If I modify the critieria API to generate the query in the following fashion,
select * from user_info
where hobbies like '%Gardening|%' or hobbies like '%Fishing|%' or hobbies like '%Reading|%'
or hobbies like '%|Gardening|%' or hobbies like '%|Fishing|%' or hobbies like '%|Reading|%'
or hobbies like '%|Gardening%' or hobbies like '%|Fishing%' or hobbies like '%|Reading%';
This design also has a flaw. The only way to solve this by using FIND_IN_SET.
Mysql query:
select * from user_info
where hobbies find_in_set('Gardening', replace(hobbies, '|', ','))
or hobbies find_in_set('Fishing', replace(hobbies, '|', ','))
or hobbies find_in_set('Reading', replace(hobbies, '|', ','));
How to create a find_in_set query using Hibernate Criteria? Using #Formula ?
If your goal is building query dynamically and not using Criteria API, you may build it with FluentJPA, like in this:
public List<UserInfo> filterByHobbies(List<String> hobbies) {
Function1<UserInfo, Boolean> dynamicFilter = buildOr(hobbies);
FluentQuery query = FluentJPA.SQL((UserInfo user) -> {
SELECT(user);
FROM(user);
WHERE(dynamicFilter.apply(hobbies);
});
return query.createQuery(getEntityManager(), UserInfo.class).getResultList();
}
private Function1<UserInfo, Boolean> buildOr(List<String> hobbies) {
Function1<UserInfo, Boolean> criteria = Function1.FALSE();
for (String hobby : hobbies)
criteria = criteria.or(u -> FIND_IN_SET(parameter(hobby),
u.getHobbies().replace('|', ',')) > 0);
return criteria;
}
This is an option in some cases:
xxxRepository.findAll((root, query, cb) -> {
List<Predicate> predicateList = new ArrayList<>();
if (StringUtils.isNotBlank(param)) {
Expression<Integer> findInSetFun = cb.function("FIND_IN_SET", Integer.class,
cb.literal(param), root.get("targetColName"));
predicateList .add(cb.greaterThan(findInSetFun, 0));
}
}, pageable);
Expression<Integer> function = criteriaBuilder.function("find_in_set", Integer.class,
criteriaBuilder.literal(warehouseList), root.get("sWarehouseIdList"));
Predicate sWarehouseIdList = criteriaBuilder.greaterThan(function, 0);
predicateList.add(sWarehouseIdList);

Select table row using HQL with param

I want to use this HQL query in order to select table row value:
String hql = "select e.token, e.name from Terminals e where e.token = ?";
Terminals terminal = entityManager
.createQuery(hql, Terminals.class)
.setParameter(0, terminalToken)
.getSingleResult();
But I get this result:
java.lang.IllegalArgumentException: Cannot create TypedQuery for query with more than one return using requested result type [org.api.entity.Terminals]
What is the proper way to implement this?
Your query return two Objects token and name and not a Terminals Object.
Instead you can use :
Object[] obj = entityManager
.createQuery(hql)
.setParameter(0, terminalToken)
.getSingleResult();
if(obj != null){
Terminals terminal = new Terminals((String) obj[0], (String) obj[1]);
}
Or you can create a constructor in Terminals class which hold two fields :
public Terminals(String token, String name){
this.token = token;
this.name = name;
}
then change your code to be :
String hql = "select new com.package.Terminals(e.token, e.name) from Terminals e where e.token = ?";
Terminals terminal = entityManager
.createQuery(hql, Terminals.class)
.setParameter(0, terminalToken)
.getSingleResult();
IMO, you should not directly call createQuery to assign to a custom object. And there is a possiblity of returning a list.
Query query = entityManager.createQuery(hql).setParameter(0, "something");<br>
List<Object[]> terminals = query.getResultList();
and retrieve the objects with an index as the ff as an example:
StudentDto dto = new StudentDto();
for(Object[] a: std) {
dto = new StudentDto();
dto.setId((long) a[0]); //id
dto.setName(a[1].toString()); //first name
dto.setPassportNumber(a[2].toString()); //last name
}

How to get data from database using hibernate template

Hi, Is it possible how to get data database tables ,column names and column
values etc ?
I have tried sample snippet code please let me know how to get database column names and values ?
public List<String> getAllTables(){
Map<String, ClassMetadata> classMetaDataMap =
hibernateTemplate.getSessionFactory().getAllClassMetadata();
for(Map.Entry<String, ClassMetadata> metaDataMap : classMetaDataMap.entrySet()) {
ClassMetadata classMetadata = metaDataMap.getValue();
AbstractEntityPersister abstractEntityPersister = (AbstractEntityPersister) classMetadata;
String tableName = abstractEntityPersister.getTableName();
}
}
In hibernate for getting column names use the following:
ClassMetadata classMetadata = sessionFactory.getClassMetadata(Person.class);
String[] propertyNames = classMetadata.getPropertyNames();
And for getting column values you can use Hibernate Criteria.
Criteria criteria = session.createCriteria(Person.class);
List list = criteria.list();

Multiple filter in sql query (JDBC)

I came up with the solution and wanted to know any other better approach to handle this problem ?
I am not using hibernate instead using JDBC template.
I've Employee table with following attributes
id (Auto generated Primary key)
first_name
last_name
salary
Requirement :
1.Write getByFilter API in EmployeeDAO.
2.If any of the field is null in the filter object ignore that in the query.
Solution :
I came up with following generic solution.
public List<Employee> getByFilter(Employee filter){
NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(
getDataSource());
String query = "SELECT ID as id,"+
"first_name as firstName,"+
"last_name as lastName,"+
"dob as dob ,"+
"department_name as departmentName,"+
"salary as salary"+
"FROM employee "+
"WHERE "+
"(:id IS NULL OR id = :id ) AND"+ // Handling second requirement if field is null
"(:first_name IS NULL or first_name = :firstName ) AND"+
"(:last_name IS NULL or first_name = :lastName) AND"+
"(:salary IS NULL OR salary = :salary)";
Map<String, Object> aMap = new HashMap<String, Object>();
aMap.put("id", filter.getId());
aMap.put("firstName", filter.getFirstName());
aMap.put("lastName", filter.getLastName());
aMap.put("salary", filter.getSalary());
return namedParameterJdbcTemplate.query(getQuery, aMap,
new BeanPropertyRowMapper<Employee>(Employee.class));
}
A better option can be to use hibernate criteria so that it will reduce the query
Criteria criteria = session.createCriteria(classname);
criteria.add(Restrictions.eq("first_name", firstName));
criteria.add(Restrictions.eq("last_name", lastName));
//same for all properties
//for checking null it will be like
criteria.add(Restrictions.isNotNull("id "));
//same for others
hibernate criteria provides better options to write the database query and easier and cleaner approach

Java Criteria API: Select single column from one-to-many relationship table

I am trying to select a single column from a related table. I have a table (Item) with many Values. I would like to select Value.valueString.
Basically, the query is supposed to pass in a bunch of values and pull any ValueFields that contain those values. The SQL might look something like this:
select ItemValues.valueString from ItemEntity
join StockItem on ItemEntity.stockItemId = StockItem.id
join ItemValues on ItemEntity.id = ItemValues.itemId
where StockItem.vendor = vendorId
AND (ItemValues.valueString like '%test%' OR ItemValues.valueString like '%test2%'...);
Here is my code:
final CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
final CriteriaQuery<String> query = builder.createQuery(String.class);
final Root<ItemEntity> root = query.from(ItemEntity.class);
query.select(root.join("ItemValues").<String>get("ValueString"));
final List<Predicate> filters = new LinkedList<Predicate>();
filters.add(builder.equal(root.join("StockItem").get("id"), vendorNumber));
final List<Predicate> filterNamesCriteria = new LinkedList<Predicate>();
if (filenames.length > 0) {
for (String fileName : filenames) {
filterNamesCriteria.add(builder.like(root.join("ItemValues").<String>get("ValueString"), fileName));
}
filters.add(builder.or(filterNamesCriteria.toArray(new Predicate[0])));
}
query.where(filters.toArray(new Predicate[0]));
final TypedQuery<String> resolvedQuery = this.entityManager.createQuery(query);
return resolvedQuery.getResultList();
I want the result to return a List of Strings (valueString column), but it's not returning anything.
Am I doing something wrong? When I say "builder.createQuery(String.class)", is that correct?
I found the problem:
filters.add(builder.equal(root.join("StockItem").get("id"), vendorNumber));
I was joining based on the StockItem id and not the StockItem.itemNumber
I used two queries to solve the issue of joining the Itemvalues map (it was returning 32,000+ results)

Categories

Resources