0

I am working on a spring boot project and using JPA for querying the database with Entity manager.

i.e.

Query query = entityManager.createNativeQuery("SELECT * FROM TABLE_NAME WHERE ID = 1"); // SUPPOSE

List<Object[]> result = (List<Object[]>) query.getResultList();

now after this what I want to achieve here is creating an Object of that corresponding result.

i.e.  ResultObject obj = (ResultObject) result.get(0); 
// here ResultObject is user defined.

but this type of casting is not possible directly.

so what I am currently doing is:

ResultObject obj = new ResultObject(); 
obj.setArribute1((String) obj[0]);
obj.setArribute2((Integer) obj[1]);
...

and on average i will be having 15 attributes per object. so its really tiresome...

I have tried using:

List<ResultObject> obj = (List<ResultObject>)query.getResultList();

but doesn't work.

4
  • I think a similar question is mentioned here. stackoverflow.com/questions/13700565/… Commented Jul 17, 2020 at 13:49
  • So use JPA as it's intended and use a JPQL query of SELECT new ResultClass... Commented Jul 17, 2020 at 13:50
  • @chrylis-cautiouslyoptimistic- You can't use constructor expression with native queries Commented Jul 17, 2020 at 13:51
  • @SimonMartinelli I meant to say "JPQL query" (updated), but that was the exact point of "JPQL". Commented Jul 17, 2020 at 13:52

4 Answers 4

0

Either use ConstructorResult (JPA) or ResultTransformer (Hibernate) or QLRM.

ConstructorResult is JPA standard and you have to create a Annotation with the column mapping:

@SqlResultSetMapping(
    name = "BookValueMapping",
    classes = @ConstructorResult(
            targetClass = BookValue.class,
            columns = {
                @ColumnResult(name = "id", type = Long.class),
                @ColumnResult(name = "title"),
                @ColumnResult(name = "version", type = Long.class),
                @ColumnResult(name = "authorName")}))

From https://thorben-janssen.com/result-set-mapping-constructor-result-mappings/

And ResultTransformer is Hibernate proprietary and you must use the Hibernate session:

List<PersonSummaryDTO> dtos = session.createNativeQuery(
    "SELECT p.id as \"id\", p.name as \"name\" " +
    "FROM Person p")
.setResultTransformer( Transformers.aliasToBean( PersonSummaryDTO.class ) )
.list();

From https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#sql-dto-query

Or QLRM is a library that maps the result to a DTO using the constructor:

JpaResultMapper jpaResultMapper = new JpaResultMapper();

Query q = em.createNativeQuery("SELECT ID, NAME FROM EMPLOYEE");
List<EmployeeTO> list = jpaResultMapper.list(q, EmployeeTO.class);

https://github.com/72services/qlrm

Sign up to request clarification or add additional context in comments.

5 Comments

qlrm works for me, but just a small issue. it does not perform wide conversion (type casting for Byte to Integer) ... It works by creating special constructor for this. thanks though...
QLRM is very basic. I decided not to implement conversion because of the other options
how can I do the one to many mapping using QLRM, is it possible? and is there some sort of documentation for this?
No that's not possible. Because the goal of QLRM is to map a result of a query to a DTO. And the result of a query is always a flat ResultSet
so what do you suggest in this case what's the alternative?
0

if you have set up a DatabaseConfig like this tutorial then you can simply create a class that you annotate with @Entity and @Table(name = "yourDatabaseTableName") Don't forget to define:

    @Id
    @Column(name = "ID")
    private Long id;

and annotate all your colums with @Column(name = "databaseColumnName") Then, create an interface that you annotate with @Repository which extends JpaRepository<YourEntityClass, Long>where the Long-parameter is the type you've given to the id-variable of your Entity.

Now you can use simple JPA-methodqueries like findAll() or you can create your own JPQL-queries like:

@Query("SELECT e FROM Entity e "
        + "WHERE e.id = :id")
Optional<Entity> findById(@Param("id") Long id);

It's even possible to use NativeQueries in this way:

@Query(value = "SELECT e FROM Entity e "
        + "WHERE e.id = :id",
        nativeQuery = true)
Optional<Entity> findById(@Param("id") Long id);

Comments

0

Id suggest creating a POJO that can be mapped to your table you're retrieving values from:

@Entity
@Table(name = "MyTable")
@NamedQueries({
  @NamedQuery(name = "MyTable.findAll", query = "SELECT m FROM MyTable m")})
public class MyTable implements Serializable {

  private static final long serialVersionUID = 1L;
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Basic(optional = false)
  @Column(name = "id")
  private Integer id;
  
  @Basic(optional = false)
  @Column(name = "name")
  private String name;
  
  @Basic(optional = false)
  @Column(name = "display_name")
  private String displayName;
  

  public MyTable() {
  }

  public MyTable(Integer id) {
    this.id = id;
  }

  public MyTable(Integer id, String name, String displayName) {
    this.id = id;
    this.name = name;
    this.displayName = displayName;
  }

  public Integer getId() {
    return id;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getDisplayName() {
    return displayName;
  }

  public void setDisplayName(String displayName) {
    this.displayName = displayName;
  }


  @Override
  public boolean equals(Object object) {
    // TODO: Warning - this method won't work in the case the id fields are not set
    if (!(object instanceof MyTable)) {
      return false;
    }
    MyTable other = (MyTable ) object;
    if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
      return false;
    }
    return true;
  }

  @Override
  public String toString() {
    return "MyTable[ id=" + id + " ]";
  }
  
}

Obviously fill in the fields as you need with the corresponding database datatype repersentation.

Notice how i have NamedQueries we can now take advantage of those named queries to do our fetches

TypedQuery<MyTable> query = entityManager.createNamedQuery("MyTable.findAll", MyTable.class);
List<MyTable> results = query.getResultList();

this will do all the casting and conversions for you. You can add all the named queries you want.

https://www.objectdb.com/java/jpa/query/named

UPDATE

If you need to dynamically create a query you can do the following:

String query = "SELECT m FROM MyTable m Where m.id =:id and m.name=:name"; 
///modify the query as needed based off of other conditions)

TypedQuery<MyTable > query = em.createQuery(query, MyTable .class);
query.setParameter("id", id);
query.setParameter("name", name);

List<MyTable> results = query.getResultList();

https://www.objectdb.com/java/jpa/query/api

2 Comments

Can named query be dynamic? I need to you different joins based on stored procedures as well is it possible to do so?
Named queries cannot be dynamic but you can craft your JPQL query and execute that. See my edit. Also can check out the link objectdb.com/java/jpa/query/api to help you further
0

You can convert Object[] array results into Map of instances which will be key value pairs as columns and their subsequent values, by using the custom algorithm I built. And later on you can use the map instance to construct your custom bean via constructor or setters.

/**
 * method to convert and transform an array of objects into a list of map instances, consisting columns as keys
 * and their subsequent values
 *
 * @param arrays - a list of object type array instances, representing the values retrieved from a SQL query
 * @param query  - a string value representing the SQL query which has retrieved the Object array values
 *
 *               <p> Note : The String Query must satisfy the below statements</p>
 *               <p> 1. Query must not contain any of \n or \t in it.</p>
 *               <p> 2. Query must not contain asterick, instead should contain all the concrete column names.</p>
 *               <p> 3. All the FROM keyword inside the query should be either fully capitalised or fully non capitalised i.e. FROM or from. </p>
 * @return
 * @author - Harsh Sharma
 */
public List<Map<String, Object>> convertQueryResultIntoMap(List<Object[]> arrays, String query) {
    // base cases
    if (CollectionUtils.isEmpty(arrays) || query == null || query.equals("")) return null;
    int lastFROMClauseIndex = -1;
    char[] arr = query.toCharArray();
    Stack<Character> stack = new Stack<>();
    for (int i = arr.length - 1; i > -1; i--) {
        if (i - 3 > -1 && ((arr[i] == 'M' && arr[i - 1] == 'O' && arr[i - 2] == 'R' && arr[i - 3] == 'F')
                || (arr[i] == 'm' && arr[i - 1] == 'o' && arr[i - 2] == 'r' && arr[i - 3] == 'f'))) {
            lastFROMClauseIndex = i - 3;
            if (stack.isEmpty()) break;
        } else {
            if (arr[i] == ')') stack.push(arr[i]);
            else if (arr[i] == '(') stack.pop();
        }
    }

    List<String> columnNames = new ArrayList<>();
    List<Map<String, Object>> mapList = new ArrayList<>();
    for (int i = 0; i < arr.length; i++) {
        StringBuilder columnName = new StringBuilder("");
        if (i < arr.length - 1 && arr[i] == ',') {
            int anyAlphabetEncounteredIndex = -1;
            int lastAlphabetEncounteredIndex = -1;
            int j = i - 1;
            for (; j > -1; j--) {
                if (anyAlphabetEncounteredIndex != -1 && (arr[j] == ' ' || arr[j] == '.')) break;
                if (arr[j] != ' ') {
                    if (lastAlphabetEncounteredIndex == -1) lastAlphabetEncounteredIndex = j;
                    anyAlphabetEncounteredIndex = j;
                }
            }
            for (; anyAlphabetEncounteredIndex <= lastAlphabetEncounteredIndex; anyAlphabetEncounteredIndex++) {
                columnName.append(arr[anyAlphabetEncounteredIndex]);
            }
            if (!columnName.toString().equals("")) {
                columnNames.add(columnName.toString());
            }
        } else if (i == lastFROMClauseIndex) {
            int anyAlphabetEncounteredIndex = -1;
            int lastAlphabetEncounteredIndex = -1;
            int j = i - 1;
            for (; j > -1; j--) {
                if (anyAlphabetEncounteredIndex != -1 && (arr[j] == ' ' || arr[j] == '.')) break;
                if (arr[j] != ' ' && arr[j] != '.') {
                    if (lastAlphabetEncounteredIndex == -1) lastAlphabetEncounteredIndex = j;
                    anyAlphabetEncounteredIndex = j;
                }
            }
            for (; anyAlphabetEncounteredIndex <= lastAlphabetEncounteredIndex; anyAlphabetEncounteredIndex++) {
                columnName.append(arr[anyAlphabetEncounteredIndex]);
            }
            if (!columnName.toString().equals("")) {
                columnNames.add(columnName.toString());
            }
            break;
        }
    }

    if (!CollectionUtils.isEmpty(arrays) && !CollectionUtils.isEmpty(columnNames)) {
        int columnSize = columnNames.size();
        for (Object[] dbData : arrays) {
            Map<String, Object> map = new LinkedHashMap<>();
            for (int i = 0; i < columnSize; i++) {
                map.put(columnNames.get(i), dbData[i]);
            }
            mapList.add(map);
        }
    }
    return mapList;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.