本文所述的多表查询仅在各个表无外键等约束关系下的查询,不涉及join fetch,这里提供以下三种方法进行SSH中的多表查询:
1 采用SQL语句,将结果保存至List中,利用列表下标进行显示
2 采用HQL语句,将查询结果拆分成ORM后的各个对象进行显示
3 采用HQL语句,用HashMap接收查询结果
数据库表结构
在本例中,我们新建两张数据表,其中表的结果如下所示:
–创建学生表
create table stu3401(
id bigint primary key identity(1,1),
name varchar(50) not null,
password varchar(50) not null
);
–创建学生详细信息表
create table stuDetail(
id int primary key identity(1,1),
stuId bigint,
address varchar(100),
school varchar(50),
)
SQL脚本如下:
–在表stu3401中插入10条数据
insert into stu3401 values(‘a’,3401);
insert into stu3401 values(‘b’,3401);
insert into stu3401 values(‘c’,3401);
insert into stu3401 values(‘d’,3401);
insert into stu3401 values(‘e’,3401);
insert into stu3401 values(‘f’,3401);
insert into stu3401 values(‘g’,3401);
insert into stu3401 values(‘h’,3401);
insert into stu3401 values(‘i’,3401);
insert into stu3401 values(‘j’,3401);
–在表stuDetail中插入10条数据
insert into stuDetail values(1,’桂林市金鸡路1号’,'桂林电子科技大学a校区’);
insert into stuDetail values(2,’桂林市金鸡路2号’,'桂林电子科技大学b校区’);
insert into stuDetail values(3,’桂林市金鸡路3号’,'桂林电子科技大学c校区’);
insert into stuDetail values(4,’桂林市金鸡路4号’,'桂林电子科技大学d校区’);
insert into stuDetail values(5,’桂林市金鸡路5号’,'桂林电子科技大学e校区’);
insert into stuDetail values(6,’桂林市金鸡路6号’,'桂林电子科技大学f校区’);
insert into stuDetail values(7,’桂林市金鸡路7号’,'桂林电子科技大学g校区’);
insert into stuDetail values(8,’桂林市金鸡路8号’,'桂林电子科技大学h校区’);
insert into stuDetail values(9,’桂林市金鸡路9号’,'桂林电子科技大学i校区’);
insert into stuDetail values(10,’桂林市金鸡路10号’,'桂林电子科技大学j校区’);
1 采用SQL语句,将结果保存至List中,利用列表下标进行显示
1.1 概况
这种方法采用SQL语句,未将数据表的关系(R)进行对象(O)映射(M),因此,我们在此将查询得到的数据用ArrayList进行接收,并在前台用列表的下标进行显示,具体操作如下:
1.2 DAO层
首先我们采用SQL语句进行查询,其中DAO中代码如下:
/*
* 使用SQL语言对数据库进行查询,将结果返回至一个列表中
* */
public List queryBySQL(String queryString)
{
log.debug(“query data using function createSQLQuery and return a list”);
try {
return this.getSession().createSQLQuery(queryString).list();
} catch (RuntimeException re) {
// TODO: handle exception
log.error(“query error”,re);
throw re;
}
}
1.3 业务层
Action中的代码如下:
public ActionForward showByListAndHqlOjbject(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
String queryString=”select * from stu3401 s,stuDetail t where s.id=t.stuId”;
List stuList = stu3401Service.queryBySQL(queryString);
request.setAttribute(“result”, stuList);
return mapping.findForward(“success”);
}
通过Debug我们可以发现,采用这种方法我们得到的stuList是一个ArrayList,其值如下所示:
[[1, a, 3401, 1, 1, 桂林市金鸡路1号, 桂林电子科技大学a校区],
[2, b, 3401, 2, 2, 桂林市金鸡路2号, 桂林电子科技大学b校区],
[3, c, 3401, 3, 3, 桂林市金鸡路3号, 桂林电子科技大学c校区]
[4, d, 3401, 4, 4, 桂林市金鸡路4号, 桂林电子科技大学d校区],
[5, e, 3401, 5, 5, 桂林市金鸡路5号, 桂林电子科技大学e校区],
[6, f, 3401, 6, 6, 桂林市金鸡路6号, 桂林电子科技大学f校区],
[7, g, 3401, 7, 7, 桂林市金鸡路7号, 桂林电子科技大学g校区],
[8, h, 3401, 8, 8, 桂林市金鸡路8号, 桂林电子科技大学h校区],
[9, i, 3401, 9, 9, 桂林市金鸡路9号, 桂林电子科技大学i校区],
[10, j, 3401, 10, 10, 桂林市金鸡路10号, 桂林电子科技大学j校区],null]
它是一个长度为11的数组列表obj1[11](ArrayList,使用数组实现List数据结构,实质上就是一个长度可变的数组):该列表可描述为Object[11] obj1 = {obj10[7],obj11[7],…obj19[7],null}(注意最后还有一个null,但是其size为10,modCount也为 10),其中的每一个元素是一个长度为7的Object类型的对象列表(数组),这里表示为obj1j[7],(j=0,1,…9)。我们以j = 0为例,对于对象列表obj10[7],它包含7个属性,分别对应两张数据表中的7个字段,其中的每个字段都被强制类型转换成Object类型(因为 Object类是所有类的父类,)。因此,stuList就可理解为一个对象列表(数组)的列表(数组)。
1.4 表示层
前台JSP页面代码如下:
<table width=”600px” align=”center”>
<thead>
<tr>
<td width=”100px”>账号</td><td width=”300px”>家庭住址</td><td width=”300px”>学校</td>
</tr>
</thead>
<tbody>
<c:if test=”${!empty result}”>
<c:forEach items=”${result}” var=”Item”>
<tr>
<td>${Item[1]}</td>
<td>${Item[5]}</td>
<td>${Item[6]}</td>
</tr>
</c:forEach>
</c:if>
<c:if test=”${empty result}”>
<tr>
<td colspan=”20″ align=”center” bgcolor=”#EFF3F7″
onmouseover=”this.bgColor = ‘#DEE7FF’;”
onmouseout=”this.bgColor=’#EFF3F7′;”>
没有找到相应的记录
</td>
</tr>
</c:if>
</tbody>
</table>
1.5 小结
这种方法的弊端在于当我们对数据库进行维护时,若需要更改(如添加或删除)某些字段,则在前台JSP页面中,我们还需要对其下标进行修改,一方面给开发维护带来很大不便,另一方面也容易出错。
2采用HQL语句,将查询结果拆分成ORM后的各个对象进行显示
2.1 概况
与方法一不同,这种方法对数据表的关系进行了ORM映射,也就是我们可以对查询的结果进行对象的操作。在本例中,因为涉及到两张表的查询,因此我们需要对查询结果进行分拆后再强制类型转换,具体实现如下:
2.2 DAO层
以下是DAO层的代码:
/*
* 使用SQL语言对数据库进行查询,将结果返回至一个列表中
* */
public List queryByHql(String queryString)
{
log.debug(“query data from two tables and show data using hql object”);
try {
return getHibernateTemplate().find(queryString);
} catch (RuntimeException re) {
// TODO: handle exception
log.error(“query error”,re);
throw re;
}
}
2.3 业务逻辑层
Action中的代码如下:
public ActionForward showByListAndHqlOjbject(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
String queryString=”from Stu3401 s,StuDetail t where s.id=t.stuId”;
List stuList = stu3401Service.queryByHql(queryString);
List<Stu3401> stuList1=new ArrayList<Stu3401>();
List<StuDetail> stuList2=new ArrayList<StuDetail>();
Object[] obj = null;
for(int i=0;i<stuList.size();i++)
{
obj = (Object[])stuList.get(i);
stuList1.add((Stu3401)obj[0]);
stuList2.add((StuDetail)obj[1]);
}
request.setAttribute(“result1″, stuList1);//保存表1数据
request.setAttribute(“result2″, stuList2);//保存表2数据
return mapping.findForward(“success”);
}
通过Debug我们可以发现,采用这种方法我们得到的stuList是一个ArrayList,其值如下所示:
[[com.lyw.loginCoridc.model.domain.Stu3401@130c35c, com.lyw.loginCoridc.model.domain.StuDetail@db8522], [com.lyw.loginCoridc.model.domain.Stu3401@19a64, com.lyw.loginCoridc.model.domain.StuDetail@13f54ae],
……
……
[com.lyw.loginCoridc.model.domain.Stu3401@5b77c, com.lyw.loginCoridc.model.domain.StuDetail@151685f], null]
它也是一个长度为11的数组列表,假设定义为obj1[11] = {obj10[2],obj11[2],…,obj19[2],null},其中的每一个元素是一个长度为2的Ojbect类型的对象列表(数组),这里表示为obj1j[2],(j=0,1,…9)。我们以j = 0为例,对于对象列表obj10[2],其第一个元素是第一张数据表所映射的Stu3401类型的对象,包括id,name和password三个属性;第二个元素是第二张数据表所映射的StuDetail类型的对象,包括id,address,school和stuId四个属性。因此我们可以通过对象对查询进过进行保存,代码如上。
2.4 表示层
前台JSP页面代码如下:
<table width=”600px” align=”center”>
<thead>
<tr>
<td width=”100px”>账号</td><td width=”300px”>家庭住址</td><td width=”300px”>学校</td>
</tr>
</thead>
<tbody>
<c:if test=”${!(empty result1 || empty result2)}”>
<c:forEach items=”${result1}” var=”Item1″ varStatus=”s”>
<c:forEach items=”${result2}” var=”Item2″ begin=”${s.index}” step=”${fn:length(result1)-s.index}”>
<tr>
<td>${Item1.name}</td>
<td>${Item2.address}</td>
<td>${Item2.school}</td>
</tr>
</c:forEach>
</c:forEach>
</c:if>
<c:if test=”${empty result1 || empty result2}”>
<tr>
<td colspan=”20″ align=”center” bgcolor=”#EFF3F7″
onmouseover=”this.bgColor = ‘#DEE7FF’;”
onmouseout=”this.bgColor=’#EFF3F7′;”>
没有找到相应的记录
</td>
</tr>
</c:if>
</tbody>
</table>
在上面代码中,为了求得列表的长度,我们引入了标签fn,因此还需在页面上导入JSTL标签库:
<%@ taglib prefix=”fn” uri=”http://java.sun.com/jsp/jstl/functions” %>
关于fn标签函数的函数说明可参见:http://www.coridc.com/archives/2349.html
2.5 小结
这种方法相对第一种方法来讲,最大的好处就是能够通过对象的属性进行显示,而不再担心下标的问题。但该方法也存在一个问题,当查询所涉及到N张表时,我们就需要向前台传入N个参数,而且在前台的Foreach循环控制上,会显得很麻烦。
3 采用HQL语句,用HashMap接收查询结果
3.1 概况
在方法二中,我们从后台传递N(N表示数据查询所涉及到的表的数量)个列表至前台,由此带来的不便在2.5节中已略有阐述。在方法三中,我们将采用HashMap来接收查询结果并传递到前台,详细操作如下:
3.2 DAO层
因为方法三与方法二都采用HQL语句查询,因此方法三的DAO层代码与方法二相同。
3.3 业务逻辑层
Action中的代码如下:
public ActionForward showByListAndHqlOjbject(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
String queryString1=”select new map(s.name as name,t.address as address,t.school as school) from Stu3401 s,StuDetail t where s.id=t.stuId”;
List stuList = stu3401Service.queryByHql(queryString1);
request.setAttribute(“result”, stuList);
return mapping.findForward(“success”);
}
通过Debug我们可以发现,采用这种方法我们得到的stuList是一个ArrayList,其值如下所示:
[{address=桂林市金鸡路1号, school=桂林电子科技大学a校区, name=a},
{address=桂林市金鸡路2号, school=桂林电子科技大学b校区, name=b},
{address=桂林市金鸡路3号, school=桂林电子科技大学c校区, name=c},
{address=桂林市金鸡路4号, school=桂林电子科技大学d校区, name=d},
{address=桂林市金鸡路5号, school=桂林电子科技大学e校区, name=e},
{address=桂林市金鸡路6号, school=桂林电子科技大学f校区, name=f},
{address=桂林市金鸡路7号, school=桂林电子科技大学g校区, name=g},
{address=桂林市金鸡路8号, school=桂林电子科技大学h校区, name=h},
{address=桂林市金鸡路9号, school=桂林电子科技大学i校区, name=i},
{address=桂林市金鸡路10号, school=桂林电子科技大学j校区, name=j}]
它是一个长度为10的数组列表,列表的每一个元素都是一个HashMap<K,V>,如stuList[0] = {address=桂林市金鸡路1号, school=桂林电子科技大学a校区, name=a},那么我们可以在JSP页面直接通过HashMap的Key来取得相对应的Value,前台代码见3.4节。
3.4 表示层
前台JSP页面代码如下:
<table width=”600px” align=”center”>
<thead>
<tr>
<td width=”100px”>账号</td><td width=”300px”>家庭住址</td><td width=”300px”>学校</td>
</tr>
</thead>
<tbody>
<c:if test=”${!empty result}”>
<c:forEach items=”${result}” var=”Item”>
<tr>
<td>${Item.name}</td>
<td>${Item.address}</td>
<td>${Item.school}</td>
</tr>
</c:forEach>
</c:if>
<c:if test=”${empty result}”>
<tr>
<td colspan=”20″ align=”center” bgcolor=”#EFF3F7″
onmouseover=”this.bgColor = ‘#DEE7FF’;”
onmouseout=”this.bgColor=’#EFF3F7′;”>
没有找到相应的记录
</td>
</tr>
</c:if>
</tbody>
</table>
3.5 小结
很明显,这种方法比第一种和第二种方法要简便的多,而且易于维护,代码量小,剩下的,你们懂的。