Java class - how to pass Generic Object to a function - java

This is my basic function:
public static void main(String[] a) throws Exception {
Session sessione = HibernateUtil.getSessionFactory().openSession();
Query query = sessione.createSQLQuery("select * from User").addEntity(User.class);
List<User> rows = query.list();
Iterator it = rows.iterator();
while (it.hasNext()) {
User usr = (User) it.next();
System.out.println(usr.getEmail());
System.out.println(usr.getName());
System.out.println(usr.getIdUser());
System.out.println(usr.getUser());
}
This function is capable to connect and perform a query on my DB...
I want to create the same function but more general... The previous was specific for only one table (User), the new one must be able to accept as input a String parameter for the query, and the class type where the query will be executed. This will allow me to use only one row in order to perform a query.
It should be something like this:
public static void queryResult(String query, <ClassOfTable>) {
Session sessione = HibernateUtil.getSessionFactory().openSession();
Query qy = sessione.createSQLQuery(query).addEntity(<ClassOfTable>.class);
List<<ClassOfTable>> rows = qy.list();
Iterator it = rows.iterator();
while (it.hasNext()) {
<ClassOfTable> obj = (<ClassOfTable>) it.next();
}
}
Where you find ClassOfTable I don't know how to "generalize" the code...
I hope to have been clear...
P.S. ClassOfTable should be the class rappresentative of a table in DB (Hibernate).
Thanks.

public static <T> void queryResult(String query, Class<? extends T> clazz) {
Session session = HibernateUtil.getSessionFactory().openSession();
Query q = session.createSQLQuery(query).addEntity(clazz);
List rows = q.list();
Iterator it = rows.iterator();
while (it.hasNext()) {
T t = (T) it.next();
// do your work on object t
}
}
If your intention is to return resultset, use:
public static <T> List<T> queryResult(String query, Class<? extends T> clazz) {
Session session = HibernateUtil.getSessionFactory().openSession();
Query q = session.createSQLQuery(query).addEntity(clazz);
List<T> rows = (List<T>) q.list();
return Collections.unmodifiableList(rows);
}
// now call generic method
List<User> users = queryResult("select * from User", User.class);
users.forEach(usr -> {
System.out.println(usr.getEmail());
System.out.println(usr.getName());
System.out.println(usr.getIdUser());
System.out.println(usr.getUser());
});

Related

Implementing generic DAO interface, return type of class implementing it must be cast to type T? why

I am implementing a DAO for my first full stack java program and I am a bit confused when trying to implement this generic DAO interface. See below
public interface IDataAccessDAO<T> {
List<T> selectAll();
T selectSingle(int id);
void insert(T t);
void update(T t, String[] params);
void delete(T t);
}
****************
#Override
public List<T> selectAll() {
List<Admin> allAdmins = new ArrayList<>();
try {
//Create a connection to DB and prepare select statement
Connection myConnect = this.getConnection()
PreparedStatement ps = myConnect.prepareStatement(selectAllAdmin);
//Store query result to build list of admins
ResultSet queryResult = ps.executeQuery();
while(queryResult.next()){
int id = queryResult.getInt("adminId");
String name = queryResult.getString("name");
String email = queryResult.getString("email");
String userName = queryResult.getString("userName");
String password = queryResult.getString("password");
Admin admin = new Admin(id, name, userName, password, email);
allAdmins.add(admin);
return (List<T>) allAdmins;
}
My question is, why can I not just return allAmins instead of casting it to a generic. I thought the point of generics was to create flexibility in the return types of a class?

Getting column names from a JPA Native Query

I have an administrative console in my web application that allows an admin to perform a custom SQL SELECT query on our database.
Underneath, the application is using Hibernate, but these queries are not HQL, they're pure SQL, so I'm using a Native Query like this:
protected EntityManager em;
public List<Object[]> execute(String query) {
Query q = em.createNativeQuery(query);
List<Object[]> result = q.getResultList();
return result;
}
This works correctly, but it only returns the rows of data, with no extra information. What I would like is to also get the column names, so when I print the results back to the user I can also print a header to show what the various columns are.
Is there any way to do this?
Query query = entityManager.createNamedQuery(namedQuery);
NativeQueryImpl nativeQuery = (NativeQueryImpl) query;
nativeQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> result = nativeQuery.getResultList();
And now you have Map<String,Object> . You can see your Column Names
2020
With hibernate 5.2.11.Final is actually pretty easy.
In my example you can see how I get the column names for every row. And how I get values by column name.
Query q = em.createNativeQuery("SELECT columnA, columnB FROM table");
List<Tuple> result = q.getResultList();
for (Tuple row: result){
// Get Column Names
List<TupleElement<Object>> elements = row.getElements();
for (TupleElement<Object> element : elements ) {
System.out.println(element.getAlias());
}
// Get Objects by Column Name
Object columnA;
Object columnB;
try {
columnA = row.get("columnA");
columnB= row.get("columnB");
} catch (IllegalArgumentException e) {
System.out.println("A column was not found");
}
}
This code worked for me
DTO Class :
public class ItemResponse<T> {
private T item;
public ItemResponse() {
}
public ItemResponse(T item) {
super();
this.item = item;
}
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}
Service Class is in the below
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.stereotype.Service;
import org.hibernate.transform.AliasToEntityMapResultTransformer;
#Service
public class ServiceClass{
#PersistenceContext
public EntityManager entityManager;
public ItemResponse exceuteQueryResponse(String queryString) {
ItemResponse itemResponse=new ItemResponse();
Query jpaQuery = entityManager.createNativeQuery(queryString);
org.hibernate.Query hibernateQuery =((org.hibernate.jpa.HibernateQuery)jpaQuery).getHibernateQuery();
hibernateQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> res = hibernateQuery.list();
itemResponse.setItem(res);
return itemResponse;
}
}
Ryiad's answer DTO adds some confusion, you should have kept it away.
You should have explained that it works only with hibernate.
If like me you needs to keep the order of columns, you can specify your own transformer. i copied the code from hibernate and changed the HashMap to LinkedHashMap:
import java.util.LinkedHashMap;
import java.util.Map;
import org.hibernate.transform.AliasedTupleSubsetResultTransformer;
import org.hibernate.transform.ResultTransformer;
/**
* {#link ResultTransformer} implementation which builds a map for each "row", made up of each aliased value where the
* alias is the map key. Inspired by {#link org.hibernate.transform.AliasToEntityMapResultTransformer}, but kepping the
* ordering of elements.
* <p/>
* Since this transformer is stateless, all instances would be considered equal. So for optimization purposes we limit
* it to a single, singleton {#link #INSTANCE instance}.
*/
public class AliasToEntityMapResultTransformer extends AliasedTupleSubsetResultTransformer {
public static final AliasToEntityMapResultTransformer INSTANCE = new AliasToEntityMapResultTransformer();
/**
* Disallow instantiation of AliasToEntityMapResultTransformer.
*/
private AliasToEntityMapResultTransformer() {
}
#Override
public Object transformTuple(Object[] tuple, String[] aliases) {
Map result = new LinkedHashMap<>(tuple.length);
for (int i = 0; i < tuple.length; i++) {
String alias = aliases[i];
if (alias != null) {
result.put(alias, tuple[i]);
}
}
return result;
}
#Override
public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
return false;
}
/**
* Serialization hook for ensuring singleton uniqueing.
*
* #return The singleton instance : {#link #INSTANCE}
*/
private Object readResolve() {
return INSTANCE;
}
}
With this transformer you can used Ryiad's solution with Hibernate:
Query jpaQuery = entityManager.createNativeQuery(queryString);
org.hibernate.Query hibernateQuery =((org.hibernate.jpa.HibernateQuery)jpaQuery).getHibernateQuery();
hibernateQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> res = hibernateQuery.list();
After a long time without any answer, And based on my own further research, It seems that it can not be possible, Unfortunately.
If the JPA provider does not support the retrieval of query metadata, another solution could be the use of a SQL parser like JSQLParser, ZQL or General SQL Parser (comercial), which extracts the fields from the SELECT statement.
cast query to hibernate query, then use hibernate method
//normal use, javax.persistence.Query interface
Query dbQuery = entityManager.createNativeQuery(sql);
//cast to hibernate query
org.hibernate.Query hibernateQuery =((org.hibernate.jpa.HibernateQuery)dbQuery)
.getHibernateQuery();
hibernateQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> res = hibernateQuery.list();
List<TxTestModel> txTestModels = new ArrayList<>();
res.forEach(e->{
TxTestModel txTestModel = new ObjectMapper().convertValue(e, TxTestModel.class);
// txTestModels.add(new TxTestModel().setIdd((Integer) e.get("idd")).setMmm((String) e.get("mmm")).setDdd((Date) e.get("ddd")));
txTestModels.add(txTestModel);
});
System.out.println(txTestModels.size());
To enforce em.createNativeQuery(..).getResultList() to return List<Tuple> specify it with Tuple.class when creating native queries :
Query q = em.createNativeQuery("SELECT columnA, columnB FROM table", Tuple.class );
List<Tuple> result = q.getResultList();
for (Tuple row: result){
// Get Column Names
List<TupleElement<Object>> elements = row.getElements();
for (TupleElement<Object> element : elements ) {
System.out.println(element.getAlias());
}
// Get Objects by Column Name
Object columnA;
Object columnB;
try {
columnA = row.get("columnA");
columnB= row.get("columnB");
} catch (IllegalArgumentException e) {
System.out.println("A column was not found");
}
}
This worked for me:
final Query emQuery = em.createNativeQuery(query, Tuple.class);
final List<Tuple> queryRows = emQuery.getResultList();
final List<Map<String, Object>> formattedRows = new ArrayList<>();
queryRows.forEach(row -> {
final Map<String, Object> formattedRow = new HashMap<>();
row.getElements().forEach(column -> {
final String columnName = column.getAlias();
final Object columnValue = row.get(column);
formattedRow.put(columnName, columnValue);
});
formattedRows.add(formattedRow);
});
return formattedRows;
This is the working solution
Below example return the objectlist from the query.
Looping the same and from the first object cast it to hasmap, and hashmap.keyset will give you all the coulmn names in a set.
List dataList = session.createSQLQuery("SLECT * FROM EMPLOYEETABLE").setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
for (Object obj : dataList) {
HashMap<String, Object> hashMap = (HashMap<String, Object>) obj;
Set<String> keySet = hashMap.keySet();
break;
}
I also faced a similar problem working with JPA. There is no direct way in JPA to access the resultset metadata. The solution can be extracting column names from the query itself or use JDBC to get the metadata.

Spring Data Repositiories - add specific parameters to every query

I have a few repositories that extend org.springframework.data.mongodb.repository.MongoRepository. I have added some methods for searching entities by different parameters, however, in any search, I only want to search for entities that have the active field set to true (have opted for marking as active=false in place of deleting). For example, two sample repositories would look like this:
interface XxRepository extends MongoRepository<Xx, String> {
Optional<Xx> findOneByNameIgnoreCaseAndActiveTrue(String name)
Page<Xx> findByActiveTrue(Pageable pageable)
Xx findOneByIdAndActiveTrue(String id)
}
interface YyRepository extends MongoRepository<Yy, String> {
Optional<Yy> findOneByEmailAndActiveTrue(String email)
}
Is there any way that would allow me not to add byActiveTrue\ andActiveTrue to each and every method and set it up somewhere in one place for all the queries?
Please try this. No need to provide implementation. Change 'active' and 'email' to your db column name.
interface YyRepository extends MongoRepository<Yy, String> {
#Query(value = "{ 'active' : { '$eq': true }, 'email' : ?0 }")
Optional<Yy> findOneByEmailAndActiveTrue(#Param("email") String email)
}
You can write an template query into the MongoRepository using Criteria.
Example
class abstract MongoRepository<W, X> {
protected Class<W> typeOfEntity;
private Class<X> typeOfId;
public MongoRepository() {
typeOfEntity = (Class<W>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
typeOfId = (Class<X>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
public W get(X id) throws Exception {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<W> q = cb.createQuery(typeOfEntity);
Root<W> r = q.from(typeOfEntity);
String idName = CommonEntity.findIdFieldName(typeOfEntity);
Predicate andClose = cb.and(cb.equal(r.get("active"), true));
q.where(cb.equal(r.get(idName), id), andClose);
return em.createQuery(q).getSingleResult();
}
}
After that, you stay confident into the object running way ans stereotyping to run the good type of request.
The findIdFieldNameis an method using the #Id to get the id field name.
public abstract class CommonEntity implements Serializable {
public static String findIdFieldName(Class cls) {
for (Field field : cls.getDeclaredFields()) {
String name = field.getName();
Annotation[] annotations = field.getDeclaredAnnotations();
for (int i = 0; i < annotations.length; i++) {
if (annotations[i].annotationType().equals(Id.class)) {
return name;
}
}
}
return null;
}
}

JPA EntityManager createQuery() error

This is failing:
public List<TypeActionCommerciale> requestTypeActionCommercialeSansNip() throws PersistenceException {
Query query = createQuery("from TypeActionCommercialeImpl where type != :type1");
query.setParameter("type1", TypeActionCommercialeEnum.NIP);
return (List<TypeActionCommerciale>) query.list();
}
exception:
Hibernate: select typeaction0_.id as id1_102_, typeaction0_.libelle as
libelle3_102_, typeaction0_.code as code4_102_, typeaction0_.type as
type5_102_ from apex.typeActionCommerciale typeaction0_ where
typeaction0_.type<>?
ERROR
org.hibernate.engine.jdbc.spi.SqlExceptionHelper.logExceptions(SqlExceptionHelper.java:129)
No value specified for parameter 1 org.hibernate.exception.DataException: could not extract ResultSet at
i use setProperties but i have the same error:
public List<TypeActionCommerciale> requestTypeActionCommercialeSansNip() throws PersistenceException {
Query query = createQuery("from TypeActionCommercialeImpl where type <> :type1");
final Map<String, Object> properties = new HashMap<>();
properties.put("type1", TypeActionCommercialeEnum.NIP);
query.setProperties(properties);
return (List<TypeActionCommerciale>) query.list();
}
The problem is here query.setParameter("type1", TypeActionCommercialeEnum.NIP);
The enum type is not defined in hibernate so you must store the name of enum and use it for the query (the easy way) then use:
query.setString("type1", TypeActionCommercialeEnum.NIP.name());
To use enum directly (the hard way) you must implement your CustomUserType . You can find here how https://docs.jboss.org/hibernate/orm/5.0/manual/en-US/html/ch06.html#types-custom
The main advantages of use CustomUserType are:
you can store into DB an integer (that is more smaller) instead of a string that represents the enum.
Delegate the parsing to hibernate during storing and retrieving of object.
You can use the enum directly into the query (like you are trying to do)
Try to use <> instead of != like this:
"from TypeActionCommercialeImpl where type <> :type1"
i resolve my pb i have a class public class EnumUserType<E extends Enum<E>> implements UserType and i implement this method:
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
String name = rs.getString(names[0]);
Object result = null;
if (!rs.wasNull()) {
result = Enum.valueOf(clazz, name);
}
return result;
}
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
throws HibernateException, SQLException {
if (null == value) {
st.setNull(index, Types.VARCHAR);
} else {
st.setString(index, ((Enum<E>) value).name());
}
}

how to know the data type of list

here is my code,here i am taking elements into my list. but actually i have to process on my list . so necessary to know the type of list
public class FetchNameService {
public static Infobean fetchId() {
Infobean infobean = new Infobean();
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
String query="SELECT ifnull(max(CONVERT(substring(id,4),SIGNED) ),0) as maxId FROM infotable";
Query name = session.createSQLQuery(query);
List<?> list = name.list();
System.out.println(list.get(0));
return infobean;
}
}
You can use reflection to get the type of a class.
Class clazz = list.get(0).getClass();
System.out.println(clazz.getCanonicalName());
System.out.println(clazz.getEnclosingClass());
System.out.println(clazz.getSimpleName());
Or if you know what types of classes it could resolve to you could try something like the following:
Object o = list.get(0);
if (o instanceof String)
{
...
}
else if (o instanceof Integer)
{
...
}
I think the only thing you can try is instanceOf() with well-considered assumptions since list is of the type List<Object> according to the docs.

Categories