Java EE核心框架实战

978-7-115-36571-2
作者: 高洪岩
译者:
编辑: 傅道坤
分类: Java

图书目录:

详情

本书基于MyBatis+Hibernate+Struts2+Spring3MVC+Spring,融合了作者多年软件开发和培训的经验,面向实战,通过丰富且有参考价值的案例,展现了JavaEE开发领域中最核心、最实用的开发技术,帮助读者全面提升开发技能。

图书摘要

第2章 MyBatis 3常用技能

本章是MyBatis框架扩展的章节,本章的内容在使用MyBatis框架时一定会用到,尤其是动态SQL在使用MyBatis框架时较为常用,在本章中读者应该着重掌握如下内容:

• 使用Properties对象连接数据库。

• 动态SQL中要使用的标签。

• 动态SQL中对null值的处理。

• 对超大字符串及分页的处理。

2.1 MyBatis 3的SQL映射文件

因为MyBatis框架是基于SQL映射的,所以SQL映射文件在此框架中的位置非常重要,但好在SQL映射文件非常简单。本章将介绍SQL映射文件的具体内容及实例使用。

2.2 连接DB数据库的参数来自于Properties对象

在前面的示例中,连接数据库时的具体参数是直接在mybatis-config.xml文件中进行定义的,比如url.username和password这些信息。MyBatis还支持将这些参数值写入*.properties属性文件中。

更改mybatis-config.xml配置文件的部分代码如下。

<dataSource type= "POOLED">
<property name= "driver" value= "${drivername}" />
<property name= "url" value= "${url}" />
<property name= "username" value= "${username}" />
<property name= "password" value= "${password}" />
</dataSource>

在src中创建db.properties属性文件,内容如下。

url=jdbc:sqlserver://localhost:1079;databaseName=ghydb
drivername=com.microsoft.sqlserver.jdbc.SQLServerDriver
username=sa
password=

使用如下Java代码即可进行数据库的操作:

InputStream isRef = GetSqlSession. class
.getResourceAsStream("/db.properties");
Properties prop = new Properties();
prop.load(isRef);
String resource = "mybatis-config.xml";
InputStream inputStream = Resources. getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream, prop);
SqlSession sqlSsession = sqlSessionFactory.openSession();

2.3 <resultMap>标签

如果数据表中字段的名称和Java实体类中属性的名称不一致,就要使用<resultMap>标签来进行映射。

创建名为resultMapTest的Java项目,映射配置文件userinfoMapping.xml代码如下。

<?xml version= "1.0" encoding= "UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
<mapper namespace= "mybatis.testcurd">
<resultMap type= "entity.Userinfo" id= "userinfo">
<result column= "id" property= "idghy" />
<result column= "username" property= "usernameghy" />
<result column= "password" property= "passwordghy" />
<result column= "age" property= "ageghy" />
<result column= "insertdate" property= "insertdateghy" />
</resultMap>
<select id= "getUserinfoAll" resultMap= "userinfo">
select * from
userinfo
</select>
</mapper>

实体类Userinfo.java的结构如图2-1所示。

创建Run.java运行类,核心代码如下。

public class Run {
public static void main(String[] args) {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources. getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<Userinfo> listUserinfo = sqlSession
.selectList("getUserinfoAll");
for (int i = 0; i < listUserinfo.size(); i++) {
Userinfo userinfo = listUserinfo.get(i);
System. out.println(userinfo.getIdghy() + " "
+ userinfo.getUsernameghy() + " "
+ userinfo.getPasswordghy() + " "
+ userinfo.getAgeghy() + " "
+ userinfo.getInsertdateghy());
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

运行程序后的结果如图2-2所示。

2.4 <sql>标签

重复的SQL语句永远不可避免,<sql>标签就是用来解决这个问题的。

创建名为sqlTest的Java项目,映射配置文件userinfoMapping.xml的代码如下。

<?xml version= "1.0" encoding= "UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
<mapper namespace= "mybatis.testcurd">
<sql id= "userinfoField">id,username,password,age,insertdate</sql>
<select id= "getUserinfoAll" resultType= "map">
select
<include refid= "userinfoField" />
from
userinfo
</select>
<select id= "getUserinfoById" resultType= "map" parameterType= "int">
select
<include refid= "userinfoField" />
from
userinfo where id=#{0}
</select>
</mapper>

因为上述代码中的id、username、password和insertdate这4个字段在映射文件中多处出现,所以可以将这4个字段封装进<sql>标签中,以减少配置的代码量。

创建Run.java运行测试类,代码如下。

public class Run {
public static void main(String[] args) {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources. getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<Map> listUserinfo = sqlSession.selectList("getUserinfoAll");
for (int i = 0; i < listUserinfo.size(); i++) {
Map map = listUserinfo.get(i);
System. out.println(map.get("ID") + " " + map.get("USERNAME")
+ " " + map.get("PASSWORD") + " " + map.get("AGE")
+ " " + map.get("INSERTDATE"));
}
System. out.println("");
System. out.println("");
listUserinfo = sqlSession.selectList("getUserinfoById", 5);
for (int i = 0; i < listUserinfo.size(); i++) {
Map map = listUserinfo.get(i);
System. out.println(map.get("ID") + " " + map.get("USERNAME")
+ " " + map.get("PASSWORD") + " " + map.get("AGE")
+ " " + map.get("INSERTDATE"));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

程序运行后的结果如图2-3所示。

2.5 将SQL语句作为字符串变量传入

在MyBatis中也支持将SQL语句当成变量传入。

新建名为sqlStringVar的Java项目,映射文件userinfoMapping.xml的内容如下。

<?xml version= "1.0" encoding= "UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
<mapper namespace= "mybatis.testcurd">
<select id= "getUserinfo" parameterType= "map" resultType= "map">
select
id,username,password,age,insertdate
from
userinfo where id>#{id} order
by ${orderSql}
</select>
</mapper>

Java类文件Run.java的代码如下。

public class Run {
public static void main(String[] args) {
try {
HashMap mapParam = new HashMap();
mapParam.put("id", 5);
mapParam.put("orderSql", "id desc");
String resource = "mybatis-config.xml";
InputStream inputStream = Resources. getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<Map> listUserinfo = sqlSession.selectList("getUserinfo",
mapParam);
for (int i = 0; i < listUserinfo.size(); i++) {
Map map = listUserinfo.get(i);
System. out.println(map.get("ID") + " " + map.get("USERNAME")
+ " " + map.get("PASSWORD") + " " + map.get("AGE")
+ " " + map.get("INSERTDATE"));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

上述代码从map中get(字段名称)的形式取得字段对应的值,但字段名称在Oracle中是大写字母,所以这里的字段名称也必须要写成大写形式。为了支持方便的小写形式,可以在映射文件中为定义的SQL语句为字段指定一个别名。

select id "id",username "username",password "password",age "age"

这样从map中就可以以小写的形式取得字段值。

运行结果如图2-4所示。

2.6 动态SQL的使用

MyBatis框架还支持动态SQL标签的使用,而且某些标签的使用率还非常高。

2.6.1 插入null值时的处理第1种方法——jdbcType

创建名为dynSqlTest的Web项目,创建名为test1的Servlet对象,核心代码如下。

public class test1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("中国");
userinfo.setPassword( null);
userinfo.setAge(200L);
userinfo.setInsertdate( new Date());
SqlSession sqlSessionRef = GetSqlSession. getSqlSession();
sqlSessionRef.insert("insertUserinfo", userinfo);
sqlSessionRef.commit();
sqlSessionRef.close();
}
}

在映射文件userinfoMapping.xml中添加如下配置。

<insert id="insertUserinfo" parameterType="orm.Userinfo">
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.Long">
select idauto.nextval from dual
</selectKey>
insert into userinfo(id,username,password,age,insertdate)
values(#{id},#{username},#{password},#{age},#{insertdate})
</insert>

程序运行后出现如下异常信息。

### Error updating database. Cause: org.apache.ibatis.type.TypeException: Error setting
null for parameter #3 with JdbcType OTHER . Try setting a different JdbcType for this parameter
or a different jdbcTypeForNull configuration property. Cause: java.sql.SQLException: 无效的列类型: 1111

从出错信息中可以看到,是用null值对password字段进行了设置,造成MyBatis无法识别,这种情况可以通过设置映射的数据类型来解决。更改映射配置userinfoMapping.xml的代码如下。

<?xml version= "1.0" encoding= "UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
<mapper namespace= "mybatis.testcurd">
<insert id= "insertUserinfo" parameterType= "orm.Userinfo">
<selectKey keyProperty= "id" order= "BEFORE" resultType= "java.lang.Long">
select idauto.nextval from dual
</selectKey>
insert into userinfo(id,username,password,age,insertdate)
values(#{id,jdbcType=INTEGER},#{username,jdbcType=VARCHAR},#{password,
jdbcType=VARCHAR},#{age,jdbcType=INTEGER},#{insertdate,jdbcType=TIMESTAMP})
</insert>
</mapper>

这里在#{}格式中加入了数据类型的声明,这样可以明确地告诉MyBatis框架如果遇到null值该如何处理。再次运行程序,成功插入数据表,运行结果如图2-5所示。

2.6.2 插入null值时的处理第2种方法——<if>

通过在#{}格式中加入jdbcType即可避免插入null值时的异常,其实使用动态SQL标签也可以达到同样的效果。

在映射文件userinfoMapping.xml添加如下代码。

<insert id="insertUserinfo2" parameterType="orm.Userinfo">
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.Long">
select idauto.nextval from dual
</selectKey>
<if test="password!=null">
insert into userinfo(id,username,password,age,insertdate)
values(#{id},#{username},#{password},#{age},#{insertdate})
</if>
<if test="password==null">
insert into userinfo(id,username,age,insertdate)
values(#{id},#{username},#{age},#{insertdate})
</if>
</insert>

创建名为test2的Servlet,核心代码如下。

public class test2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Userinfo userinfo1 = new Userinfo();
userinfo1.setUsername("英国");
userinfo1.setPassword( null);
userinfo1.setAge(200L);
userinfo1.setInsertdate( new Date());
Userinfo userinfo2 = new Userinfo();
userinfo2.setUsername("法国");
userinfo2.setPassword("法国人");
userinfo2.setAge(200L);
userinfo2.setInsertdate( new Date());
SqlSession sqlSessionRef = GetSqlSession. getSqlSession();
sqlSessionRef.insert("insertUserinfo2", userinfo1);
sqlSessionRef.insert("insertUserinfo2", userinfo2);
sqlSessionRef.commit();
sqlSessionRef.close();
}
}

程序运行后,成功向数据表中插入两条记录,如图2-6所示。

2.6.3 <choose>标签的使用

<choose>标签的作用是在众多的条件中选择出一个条件,它有些类似于Java语言中switch语句的作用。

在映射文件userinfoMapping.xml中添加如下配置代码。

<select id="selectUserinfo1" parameterType="orm.Userinfo"
resultType="map">
select * from userinfo where 1=1
<choose>
<when test="username!=null">and username like '%'||#{username}||'%'</when>
<when test="password!=null">and password like '%'||#{password}||'%'</when>
<otherwise>and age=200</otherwise>
</choose>
</select>

创建名为test3的Servlet对象,核心代码如下。

public class test3 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Userinfo userinfo1 = new Userinfo();
userinfo1.setUsername("英");
Userinfo userinfo2 = new Userinfo();
userinfo2.setPassword("法");
Userinfo userinfo3 = new Userinfo();
SqlSession sqlSessionRef = GetSqlSession. getSqlSession();
List<Map> listMap1 = sqlSessionRef.selectList("selectUserinfo1",
userinfo1);
for ( int i = 0; i < listMap1.size(); i++) {
Map eachMap = listMap1.get(i);
System. out.println("listMap1中的内容: " + eachMap.get("ID") + " "
+ eachMap.get("USERNAME") + " " + eachMap.get("PASSWORD")
+ " " + eachMap.get("AGE") + " "
+ eachMap.get("INSERTDATE"));
}
List<Map> listMap2 = sqlSessionRef.selectList("selectUserinfo1",
userinfo2);
for ( int i = 0; i < listMap2.size(); i++) {
Map eachMap = listMap2.get(i);
System. out.println("listMap2中的内容: " + eachMap.get("ID") + " "
+ eachMap.get("USERNAME") + " " + eachMap.get("PASSWORD")
+ " " + eachMap.get("AGE") + " "
+ eachMap.get("INSERTDATE"));
}
List<Map> listMap3 = sqlSessionRef.selectList("selectUserinfo1",
userinfo3);
for ( int i = 0; i < listMap3.size(); i++) {
Map eachMap = listMap3.get(i);
System. out.println("listMap3中的内容: " + eachMap.get("ID") + " "
+ eachMap.get("USERNAME") + " " + eachMap.get("PASSWORD")
+ " " + eachMap.get("AGE") + " "
+ eachMap.get("INSERTDATE"));
}
sqlSessionRef.commit();
sqlSessionRef.close();
}
}

数据表userinfo中的内容如图2-7所示。

程序运行后,在控制台输出如图2-8所示的信息。

2.6.4 <set>标签的使用

<set>标签可以用在update语句中,作用是动态指定要更新的列。在映射文件userinfoMapping.xml中添加如下映射代码。

<update id="updateUserinfo" parameterType="orm.Userinfo">

update userinfo

<set>

<if test="username!=null">username=#{username},</if>

<if test="password!=null">password=#{password},</if>

<if test="age!=null">age=#{age},</if>

<if test="insertdate!=null">insertdate=#{insertdate}</if>

</set>

<if test="id!=null">where id=#{id}</if>

</update>

创建名为test4的Servlet对象,核心代码如下。

public class test4 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Userinfo userinfo = new Userinfo();
userinfo.setId(199L);
userinfo.setUsername( null);
userinfo.setPassword("新密码");
userinfo.setAge(1000L);
userinfo.setInsertdate( new Date());
SqlSession sqlSessionRef = GetSqlSession. getSqlSession();
sqlSessionRef.update("updateUserinfo", userinfo);
sqlSessionRef.commit();
sqlSessionRef.close();
}
}

程序运行结果如图2-9所示。

2.6.5 <foreach>标签的使用

<foreach>标签有循环的功能,可以用来生成有规律的SQL语句。

<foreach>标签主要的属性有item、index、collection、open、separator和close。

item表示集合中每一个元素进行迭代时的别名,index指定一个名字,用于表示在迭代过程中,每次迭代到的位置,open表示该语句以什么开始,separator表示在每次迭代之间以什么符号作为分隔符,close表示该语句以什么结束。

在映射文件userinfoMapping.xml中添加如下配置代码。

<select id= "selectUserinfo2" parameterType= "list" resultType= "map">
select * from userinfo where id in
<foreach collection= "list" item= "eachId" index= "currentIndex"
open= "(" separator= "," close= ")">
#{eachId}
</foreach>
</select>
<select id= "selectUserinfo3" parameterType= "orm.QueryUserinfo"
resultType= "map">
select * from userinfo where id in
<foreach collection= "idList" item= "eachId" index= "currentIndex"
open= "(" separator= "," close= ")">
#{eachId}
</foreach>
and username like '%'||#{username}||'%'
</select>
<select id= "selectUserinfo4" parameterType= "map" resultType= "map">
select * from userinfo where id in
<foreach collection= "idList" item= "eachId" index= "currentIndex"
open= "(" separator= "," close= ")">
#{eachId}
</foreach>
and username like '%'||#{username}||'%'
</select>

创建名为test5的Servlet,核心代码如下。

public class test5 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// id的值来自于List
List list = new ArrayList();
list.add(1);
list.add(3);
list.add(5);
SqlSession sqlSessionRef = GetSqlSession. getSqlSession();
List<Map> listMap1 = sqlSessionRef.selectList("selectUserinfo2", list);
for ( int i = 0; i < listMap1.size(); i++) {
Map eachMap = listMap1.get(i);
System. out.println("list1中的内容: " + eachMap.get("ID") + " "
+ eachMap.get("USERNAME") + " " + eachMap.get("PASSWORD")
+ " " + eachMap.get("AGE") + " "
+ eachMap.get("INSERTDATE"));
}
// id的值来自于QueryUserinfo实体中的List
QueryUserinfo queryUserinfo = new QueryUserinfo();
queryUserinfo.setUsername("法");
queryUserinfo.getIdList().add(198);
queryUserinfo.getIdList().add(199);
listMap1 = sqlSessionRef.selectList("selectUserinfo3", queryUserinfo);
for ( int i = 0; i < listMap1.size(); i++) {
Map eachMap = listMap1.get(i);
System. out.println("list2中的内容: " + eachMap.get("ID") + " "
+ eachMap.get("USERNAME") + " " + eachMap.get("PASSWORD")
+ " " + eachMap.get("AGE") + " "
+ eachMap.get("INSERTDATE"));
}
// id的值来自于Map中的List
Map paramMap = new HashMap();
paramMap.put("username", "5");
paramMap.put("idList", list);
listMap1 = sqlSessionRef.selectList("selectUserinfo4", paramMap);
for ( int i = 0; i < listMap1.size(); i++) {
Map eachMap = listMap1.get(i);
System. out.println("list3中的内容: " + eachMap.get("ID") + " "
+ eachMap.get("USERNAME") + " " + eachMap.get("PASSWORD")
+ " " + eachMap.get("AGE") + " "
+ eachMap.get("INSERTDATE"));
}
sqlSessionRef.commit();
sqlSessionRef.close();
}
}

运行程序后,控制台输出如图2-10所示的结果。

2.7 插入超大的字符串文本内容

MyBatis框架也支持Oracle的CLOB,不需要特别的环境配置即可完成对CLOB字段的操作。

创建名为bigCLOB的Web项目,映射文件userinfoMapping.xml的代码如下。

<?xml version= "1.0" encoding= "UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
<mapper namespace= "mybatis.testcurd">
<insert id= "insertBigString" parameterType= "map">
<selectKey keyProperty= "id" order= "BEFORE" resultType= "java.lang.Long">
select idauto.nextval from dual
</selectKey>
insert into bigclob(id,bigstring) values(#{id},#{bigstring})
</insert>
<select id= "selectById" resultType= "map">
select * from bigclob where
id=10000
</select>
</mapper>

创建名为test的Servlet,核心代码如下。

public class test extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
SqlSession sqlSessionRef = GetSqlSession. getSqlSession();
Map map = sqlSessionRef.selectOne("selectById");
Clob clobRef = (Clob) map.get("BIGSTRING");
Reader reader = clobRef.getCharacterStream();
BufferedReader brRef = new BufferedReader(reader);
char[] charArray = new char[2048];
int readLength = brRef.read(charArray);
while (readLength != -1) {
String bigString = new String(charArray, 0, readLength);
System. out.println(bigString);
readLength = brRef.read(charArray);
}
Map insertMap = new HashMap();
insertMap.put("id", 20000);
insertMap.put("bigstring", clobRef);
sqlSessionRef.insert("insertBigString", insertMap);
sqlSessionRef.commit();
sqlSessionRef.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

默认情况下Oracle数据表中有一条包含50万行的CLOB字段,如图2-11所示。

程序运行后,在控制台输出了最后一行信息,如图2-12所示。

数据表中还插入了另外一条50万行的记录,如图2-13所示。

2.8 分页

MyBatis还支持分页功能,创建名为pageTest的Web项目。

数据表userinfo中的记录内容如图2-14所示。

映射文件的配置代码如下。

<?xml version= "1.0" encoding= "UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
<mapper namespace= "mybatis.testcurd">
<select id= "selectPage" resultType= "map">
select * from userinfo
</select>
</mapper>

创建名为test的Servlet,代码如下:

public class test extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
SqlSession sqlSession = GetSqlSession. getSqlSession();
List<Map> listMap = sqlSession.selectList("selectPage", null,
new RowBounds(2, 4));
// 2代表从第2个记录开始,往后移动4个记录
for ( int i = 0; i < listMap.size(); i++) {
Map eachMap = listMap.get(i);
System. out.println("page中的内容: " + eachMap.get("ID") + " "
+ eachMap.get("USERNAME") + " " + eachMap.get("PASSWORD")
+ " " + eachMap.get("AGE") + " "
+ eachMap.get("INSERTDATE"));
}
}
}

程序运行后,在控制台输出如图2-15所示的结果。

相关图书

Effective Java中文版(原书第3版)
Effective Java中文版(原书第3版)
Java核心技术速学版(第3版)
Java核心技术速学版(第3版)
Java编程动手学
Java编程动手学
Java研发自测入门与进阶
Java研发自测入门与进阶
Java开发坑点解析:从根因分析到最佳实践
Java开发坑点解析:从根因分析到最佳实践
Java EE企业级应用开发实战(Spring Boot+Vue+Element)
Java EE企业级应用开发实战(Spring Boot+Vue+Element)

相关文章

相关课程