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。

目录
相关文章
|
1月前
|
存储 NoSQL Redis
【Redis】利用Redis List实现数据库分页快速查询
【Redis】利用Redis List实现数据库分页快速查询
111 0
|
1月前
|
存储 缓存 NoSQL
利用Redis List实现数据库分页快速查询的有效方法
利用Redis List实现数据库分页快速查询的有效方法
|
1月前
|
Java 数据库连接
Hibernate中使用Criteria查询及注解——(Dept.java)
Hibernate中使用Criteria查询及注解——(Dept.java)
14 1
|
1月前
|
Java 数据库连接
Hibernate中使用Criteria查询及注解——( EmpCondition)
Hibernate中使用Criteria查询及注解——( EmpCondition)
14 1
|
1月前
|
Java 数据库连接
Hibernate中使用Criteria查询及注解——(DeptTest.java)
Hibernate中使用Criteria查询及注解——(DeptTest.java)
8 1
|
1月前
|
Java 数据库连接
Hibernate中使用Criteria查询及注解——(Emp.hbm.xml)
Hibernate中使用Criteria查询及注解——(Emp.hbm.xml)
11 2
|
1月前
|
Java 数据库连接
Hibernate中使用Criteria查询及注解——(Emp.java)
Hibernate中使用Criteria查询及注解——(Emp.java)
9 0
|
3月前
|
存储 NoSQL Java
【Redis】利用 Redis List 实现 Java 数据库分页快速查询
在大型应用中,数据库分页查询是日常开发中不可避免的需求之一。随着数据量的不断增加,传统的数据库分页方式可能会变得效率较低。为了解决这一问题,本文将介绍如何使用 Redis List 数据结构,结合 Java 编程语言,实现高效的数据库分页查询。
119 9
|
4月前
|
Java
Java【代码分享 11】yaml配置List和Map参数对象的配置信息及类文件实例分享(效仿GatewayDynamic+DynamicDataSource的注入方法)
Java【代码分享 11】yaml配置List和Map参数对象的配置信息及类文件实例分享(效仿GatewayDynamic+DynamicDataSource的注入方法)
51 0
|
4月前
|
XML Java 数据库连接
Hibernate与Spring整合实践实例
Hibernate与Spring整合实践实例
40 0