Hibernate中执行NativeSQL语句查询返回自定义类型的POJO实例的List(多表查询)

简介: Hibernate中定义了hql的概念,简单地说就是,为java的码农提供了一套类似于sql的语法,但是数据表名变成了PO名,数据字段名变成了PO中属性成员名,并把这种语法称为hql。优点就是:hql看上去是面向对象的,码农不需要知道数据库中数据表的结构,只需要依据PO编写面向对象的数据库增删改查的语句。

Hibernate中定义了hql的概念,简单地说就是,为java的码农提供了一套类似于sql的语法,但是数据表名变成了PO名,数据字段名变成了PO中属性成员名,并把这种语法称为hql。
优点就是:hql看上去是面向对象的,码农不需要知道数据库中数据表的结构,只需要依据PO编写面向对象的数据库增删改查的语句。(前提是eclipse中配置了JBoss组件,从数据库,比如mysql,直接逆向工程生成eclipse中的PO代码。这样你就全面对代码,不用面对数据库了)

缺点就是,不方便,而且谁告诉你PO能解决一切了?谁告诉你所有的数据表都逆向生成PO了?

因此,Hibernate也提供了Native SQL的操作,也就是说,可以操作常见的、正常的、普通的SQL语句。

我们以查询为例,比如我们执行SQL语句进行查询,返回值是什么类型呢?查询语句怎么写呢?哈哈,今天我们看看人家代码怎么写的。

我们的目标是从下面这两个没有依赖关系的表中,联立进行查询
_
查询的结果肯定是n条记录record,我们要把n条record存放在java标准容器List中。
那么List中的每一个元素是什么类型呢?也就是数据库多表查询的每一条结果,在java代码中,也就是在内存中,存放在何种容器中呢?
答案是:自定义的POJO类型的实例。
_

表结构如下:
_
_
我们要查的字段是:
menu的mid-mcode-mtext-rcode 和 user_role的pernr
通过menu.rcode = user_role.rcode建立关系

1----创建符合查询结果的POJO
上图不上代码了,很普通的POJO,看到了5个字段与我们的目标相符
_
2----Dao
这个文件我们之前代码就公布了,现在只是列举其中负责自定义返回值NativeSQL查询的代码。

public <T extends Serializable> List<T> executeSQLQuery(String sql, Class<T> c, Object... params) {
        NativeQuery query = getSQLQuery(sql, Map.class, params, null);
        return convertMapToList(c, query.getResultList());
    }


public <T> NativeQuery getSQLQuery(String sql, Class<T> c, Object[] params1, Map<String, Object> params2) {
        
        NativeQueryImpl query = (NativeQueryImpl) getCurrentSession().createNativeQuery(sql);

        if (c == null) {
        
        } else if (c == Map.class) {
            query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        } else {
            query.setResultTransformer(Transformers.aliasToBean(c));
        }
        query.setZeroBasedParametersIndex(true);
        if (params1 != null) {
            for (int i = 0; i < params1.length; i++) {
                query.setParameter(i, params1[i]);
            }
        }
        if (params2 != null) {
            for (Map.Entry map : params2.entrySet()) {
                query.setParameter((String) map.getKey(), map.getValue());
            }
        }
        return query;
    }

    public static <T> List<T> convertMapToList(Class<T> type, List<Map<String, Object>> maps) {
        
        List<T> result = new ArrayList<T>();
        for (Map<String, Object> map : maps) {
            try {
                result.add(convertMapToBean(type, map));
            } catch (IllegalAccessException | InstantiationException | InvocationTargetException| IntrospectionException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

//                                目标自定义java类型      源数据库查询结果
    public static <T> T convertMapToBean(Class<T> type, Map<String, Object> map)
            throws IntrospectionException, IllegalAccessException, InstantiationException, InvocationTargetException 
    {
        //查询结果有多少条record,就执行多少次这个方法
        BeanInfo beanInfo = Introspector.getBeanInfo(type);
        //java自定义类型
        T obj = type.newInstance();
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        
       /*        
        //显示java自定义类型的某一个字段值 以及 setter方法名
        PropertyDescriptor dsp1 = propertyDescriptors[2];
        String sPname = dsp1.getName();
        System.out.println(sPname);
        System.out.println(dsp1.getWriteMethod().getName());
        */

        //System.out.println("----------为了看清楚设置的分割线----------");
        //遍历SQL查询语句中使用的字段名(相当于数据表字段)
        for (String key : map.keySet()) {
            
            //数据表的字段名
            String beanfieldlike = key.replaceAll("_", "").toLowerCase();
            //System.out.println(key);
            //System.out.println(beanfieldlike);
            
            //对于每一个数据表字段名,拿它和java自定义类型中的属性,进行匹配
            //propertyDescriptors.length代表自定义java类中,拥有多少个属性;
            //System.out.println("----------为了看清楚设置的分割线----------");
            for (int i = 0; i < propertyDescriptors.length; i++) {
                PropertyDescriptor descriptor = propertyDescriptors[i];
                String propertyName = descriptor.getName();
                //System.out.println(propertyName); //java自定义类中的     成员属性
                
                
                //如果配上了(class类型被排除)
                if (propertyName.toLowerCase().equals(beanfieldlike)) {
                    
                    Object value = map.get(key);
                    Object[] args = new Object[1];
                    args[0] = value;
                    
                    if (descriptor.getPropertyType() == Boolean.class) {
                        if (value != null && value.getClass() != Boolean.class) {
                            Boolean b = Boolean.valueOf(value.toString());
                            if (value instanceof Number)
                                b = Integer.parseInt(value.toString()) != 0;
                            args[0] = b;
                        }
                        
                    }
                    if (descriptor.getPropertyType() == BigInteger.class) {
                        if (value != null && value.getClass() != BigInteger.class) {
                            BigInteger b = new BigInteger(value.toString());
                            args[0] = b;
                        }
                        
                    }
                    if (descriptor.getPropertyType() == Integer.class) {
                        if (value != null) {
                            Integer b = Integer.parseInt(value.toString());
                            args[0] = b;
                        }
                        
                    }
                    //除了上述三种类型外,估计就全是String类型了
                    
                    //获取这个java自定义类型属性的setter方法,为java自定义类的实例赋值。
                    //obj是java自定义类型的实例
                    descriptor.getWriteMethod().invoke(obj, args);
                    
                }
                
                
            }
        }
        return obj;
    }

3----TestNG来测试一下:

      //测试SQL查询  多个数据表查询,测试成功
      String sql = "select a.mid, a.mcode,a.mtext,a.rcode,b.pernr from menu a,user_role b where a.rcode = b.rcode";
      List<RoleMenu> dataList = mhd.executeSQLQuery(sql, RoleMenu.class);
      int iSize = dataList.size();
      for(int i = 0;i < iSize; i++) {
          System.out.println("-------------------------");
          System.out.println("菜单编号:  "+dataList.get(i).getMid());
          System.out.println("胸卡     号:  "+dataList.get(i).getPernr());
          System.out.println("菜单     项:   "+dataList.get(i).getMtext());
      }

_

_
_Web_
以上内容发布于2018年1月25日

之后的某一天我修改了数据库结构,数据库的字段名和结构都修改了。然后2018年3月28日,我重新在自己eclipse上测试陈旧的testng代码的时候,报错,如下所示:

FAILED CONFIGURATION: @AfterMethod springTestContextAfterTestMethod(public void com.tsmi.hibernate.dao.MyHiberDaoTest.f())
org.springframework.transaction.TransactionSystemException: Could not commit Hibernate transaction; nested exception is org.hibernate.TransactionException: Transaction was marked for rollback only; cannot commit
    Caused by: org.hibernate.TransactionException: Transaction was marked for rollback only; cannot commit
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'a.mid' in 'field list'

testng的代码如下:

      String sql = "select a.mid,a.mcode,a.mtext,a.rcode,b.pernr from menu a,user_role b where a.rcode = b.rcode";
        
      List<RoleMenu> dataList = mhd.executeSQLQuery(sql, RoleMenu.class);
      int iSize = dataList.size();
      for(int i = 0;i < iSize; i++) {
          System.out.println("-------------------------");
          System.out.println("菜单编号:  "+dataList.get(i).getMid());
          System.out.println("胸卡     号:  "+dataList.get(i).getPernr());
          System.out.println("菜单     项:   "+dataList.get(i).getMname());
      }

这种eclipse中的报错信息,时隔2个月真的很难回忆。
后来我发现数据库结构变成了如下形式:
user_role

_
menu

_
于是修改RoleMenu.java

package com.tsmi.hibernate.bean;

public class RoleMenu implements java.io.Serializable{

    private static final long serialVersionUID = 1L;
    
    private String menuid;//菜单项编号
    private String menuname;
    private String rcode;
    private String pernr;
    public String getMenuid() {
        return menuid;
    }
    public void setMenuid(String menuid) {
        this.menuid = menuid;
    }
    public String getMenuname() {
        return menuname;
    }
    public void setMenuname(String menuname) {
        this.menuname = menuname;
    }
    public String getRcode() {
        return rcode;
    }
    public void setRcode(String rcode) {
        this.rcode = rcode;
    }
    public String getPernr() {
        return pernr;
    }
    public void setPernr(String pernr) {
        this.pernr = pernr;
    }

}

修改之后,修改对应的testng代码

      String sql = "select a.menuid,a.menuname,a.rcode,b.pernr from menu a,user_role b where a.rcode = b.rcode";
        
      List<RoleMenu> dataList = mhd.executeSQLQuery(sql, RoleMenu.class);
      int iSize = dataList.size();
      for(int i = 0;i < iSize; i++) {
          System.out.println("-------------------------");
          System.out.println("菜单编号:  "+dataList.get(i).getMenuid());
          System.out.println("胸卡     号:  "+dataList.get(i).getPernr());
          System.out.println("菜单     项:   "+dataList.get(i).getMenuname());
      }

再次运行测试代码
_
_
最终可以顺利的,从多表联合中,获取返回值为java标准容器的返回值,容器中的内容,是自定义的java类PO。

目录
相关文章
|
3月前
|
API Java 数据库连接
从平凡到卓越:Hibernate Criteria API 让你的数据库查询瞬间高大上,彻底告别复杂SQL!
【8月更文挑战第31天】构建复杂查询是数据库应用开发中的常见需求。Hibernate 的 Criteria API 以其强大和灵活的特点,允许开发者以面向对象的方式构建查询逻辑,同时具备 SQL 的表达力。本文将介绍 Criteria API 的基本用法并通过示例展示其实际应用。此 API 通过 API 构建查询条件而非直接编写查询语句,提高了代码的可读性和安全性。无论是简单的条件过滤还是复杂的分页和连接查询,Criteria API 均能胜任,有助于提升开发效率和应用的健壮性。
123 0
|
3月前
|
SQL Java 数据库连接
|
3月前
|
缓存 Java 数据库连接
什么是 Hibernate 查询语言或 HQL?
【8月更文挑战第21天】
116 0
|
3月前
|
SQL Java 数据库连接
在 Hibernate 中何时使用条件查询?
【8月更文挑战第21天】
44 0
|
3月前
|
缓存 Java 数据库连接
Hibernate 中的查询缓存是什么?
【8月更文挑战第21天】
38 0
|
3月前
|
存储 SQL Java
|
3月前
|
SQL 安全 Java
|
4月前
|
NoSQL Java Redis
Redis09-----List类型,有序,元素可以重复,插入和删除快,查询速度一般,一般保存一些有顺序的数据,如朋友圈点赞列表,评论列表等,LPUSH user 1 2 3可以一个一个推
Redis09-----List类型,有序,元素可以重复,插入和删除快,查询速度一般,一般保存一些有顺序的数据,如朋友圈点赞列表,评论列表等,LPUSH user 1 2 3可以一个一个推
|
6月前
|
SQL Java 数据库连接
Mybatis查询 出现Unknow colum 'xxxx' in field list 解决办法
Mybatis查询 出现Unknow colum 'xxxx' in field list 解决办法
|
6月前
如何将Doris的COLLECT_LIST结果用在另一个查询的条件语句里
【4月更文挑战第21天】如何将Doris的COLLECT_LIST结果用在另一个查询的条件语句里
167 0
下一篇
无影云桌面