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());
}
以上内容发布于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。