MyBatis学习总结(一)——MyBatis快速入门

一、Mybatis介绍
  
  MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
二、mybatis快速入门
2.1、准备开发环境
 1、创建测试项目,普通java项目或者是JavaWeb项目均可,如下图所示:
  
2、添加相应的jar包
  【mybatis】
         mybatis-3.1.1.jar
  【MYSQL驱动包】
    mysql-connector-java-5.1.7-bin.jar
   
3、创建数据库和表,针对MySQL数据库
  SQL脚本如下:
1 create database mybatis;
2 use mybatis;
3 CREATE TABLE users(id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20), age INT);
4 INSERT INTO users(NAME, age) VALUES(‘孤傲苍狼’, 27);
5 INSERT INTO users(NAME, age) VALUES(‘白虎神皇’, 27);
  将SQL脚本在MySQL数据库中执行,完成创建数据库和表的操作,如下:
  
  到此,前期的开发环境准备工作全部完成。
2.2、使用MyBatis查询表中的数据
  1、添加Mybatis的配置文件conf.xml
  在src目录下创建一个conf.xml文件,如下图所示:
  
  conf.xml文件中的内容如下:

1 <?xml version="1.0" encoding="UTF-8"?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

2、定义表所对应的实体类,如下图所示:
  
  User类的代码如下:

1 package me.gacl.domain;
2
3 /**
4 * @author gacl
5 * users表所对应的实体类
6 */
7 public class User {
8
9 //实体类的属性和表的字段名称一一对应
10 private int id;
11 private String name;
12 private int age;
13
14 public int getId() {
15 return id;
16 }
17
18 public void setId(int id) {
19 this.id = id;
20 }
21
22 public String getName() {
23 return name;
24 }
25
26 public void setName(String name) {
27 this.name = name;
28 }
29
30 public int getAge() {
31 return age;
32 }
33
34 public void setAge(int age) {
35 this.age = age;
36 }
37
38 @Override
39 public String toString() {
40 return “User [id=” + id + “, name=” + name + “, age=” + age + “]”;
41 }
42 }

3、定义操作users表的sql映射文件userMapper.xml
  创建一个me.gacl.mapping包,专门用于存放sql映射文件,在包中创建一个userMapper.xml文件,如下图所示:
  
  userMapper.xml文件的内容如下:

1 <?xml version="1.0" encoding="UTF-8" ?>
2
3
6
7
12
15 <select id=“getUser” parameterType=“int”
16 resultType=“me.gacl.domain.User”>
17 select * from users where id=#{id}
18
19

4、在conf.xml文件中注册userMapper.xml文件

1 <?xml version="1.0" encoding="UTF-8"?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
20
21
22
23

5、编写测试代码:执行定义的select语句
  创建一个Test1类,编写如下的测试代码:

package me.gacl.test;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import me.gacl.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Test1 {

public static void main(String[] args) throws IOException {
    //mybatis的配置文件
    String resource = "conf.xml";
    //使用类加载器加载mybatis的配置文件(它也加载关联的映射文件)
    InputStream is = Test1.class.getClassLoader().getResourceAsStream(resource);
    //构建sqlSession的工厂
    SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
    //使用MyBatis提供的Resources类加载mybatis的配置文件(它也加载关联的映射文件)
    //Reader reader = Resources.getResourceAsReader(resource); 
    //构建sqlSession的工厂
    //SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
    //创建能执行映射文件中sql的sqlSession
    SqlSession session = sessionFactory.openSession();
    /**
     * 映射sql的标识字符串,
     * me.gacl.mapping.userMapper是userMapper.xml文件中mapper标签的namespace属性的值,
     * getUser是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
     */
    String statement = "me.gacl.mapping.userMapper.getUser";//映射sql的标识字符串
    //执行查询返回一个唯一user对象的sql
    User user = session.selectOne(statement, 1);
    System.out.println(user);
}

}

执行结果如下:
  
  可以看到,数据库中的记录已经成功查询出来了。
只为成功找方法,不为失败找借口!
MyBatis学习总结(二)——使用MyBatis对表执行CRUD操作
  一、使用MyBatis对表执行CRUD操作——基于XML的实现
1、定义sql映射xml文件
  userMapper.xml文件的内容如下:

1 <?xml version="1.0" encoding="UTF-8" ?>
2
3
6
7
12
15 <select id=“getUser” parameterType=“int”
16 resultType=“me.gacl.domain.User”>
17 select * from users where id=#{id}
18
19
20
21
22 insert into users(name,age) values(#{name},#{age})
23
24
25
26
27 delete from users where id=#{id}
28
29
30
31
32 update users set name=#{name},age=#{age} where id=#{id}
33
34
35
36
37 select * from users
38
39
40

单元测试类代码如下:

1 package me.gacl.test;
2
3 import java.util.List;
4 import me.gacl.domain.User;
5 import me.gacl.util.MyBatisUtil;
6 import org.apache.ibatis.session.SqlSession;
7 import org.junit.Test;
8
9 public class TestCRUDByXmlMapper {
10
11 @Test
12 public void testAdd(){
13 //SqlSession sqlSession = MyBatisUtil.getSqlSession(false);
14 SqlSession sqlSession = MyBatisUtil.getSqlSession(true);
15 /**
16 * 映射sql的标识字符串,
17 * me.gacl.mapping.userMapper是userMapper.xml文件中mapper标签的namespace属性的值,
18 * addUser是insert标签的id属性值,通过insert标签的id属性值就可以找到要执行的SQL
19 /
20 String statement = “me.gacl.mapping.userMapper.addUser”;//映射sql的标识字符串
21 User user = new User();
22 user.setName(“用户孤傲苍狼”);
23 user.setAge(20);
24 //执行插入操作
25 int retResult = sqlSession.insert(statement,user);
26 //手动提交事务
27 //sqlSession.commit();
28 //使用SqlSession执行完SQL之后需要关闭SqlSession
29 sqlSession.close();
30 System.out.println(retResult);
31 }
32
33 @Test
34 public void testUpdate(){
35 SqlSession sqlSession = MyBatisUtil.getSqlSession(true);
36 /
*
37 * 映射sql的标识字符串,
38 * me.gacl.mapping.userMapper是userMapper.xml文件中mapper标签的namespace属性的值,
39 * updateUser是update标签的id属性值,通过update标签的id属性值就可以找到要执行的SQL
40 /
41 String statement = “me.gacl.mapping.userMapper.updateUser”;//映射sql的标识字符串
42 User user = new User();
43 user.setId(3);
44 user.setName(“孤傲苍狼”);
45 user.setAge(25);
46 //执行修改操作
47 int retResult = sqlSession.update(statement,user);
48 //使用SqlSession执行完SQL之后需要关闭SqlSession
49 sqlSession.close();
50 System.out.println(retResult);
51 }
52
53 @Test
54 public void testDelete(){
55 SqlSession sqlSession = MyBatisUtil.getSqlSession(true);
56 /
*
57 * 映射sql的标识字符串,
58 * me.gacl.mapping.userMapper是userMapper.xml文件中mapper标签的namespace属性的值,
59 * deleteUser是delete标签的id属性值,通过delete标签的id属性值就可以找到要执行的SQL
60 /
61 String statement = “me.gacl.mapping.userMapper.deleteUser”;//映射sql的标识字符串
62 //执行删除操作
63 int retResult = sqlSession.delete(statement,5);
64 //使用SqlSession执行完SQL之后需要关闭SqlSession
65 sqlSession.close();
66 System.out.println(retResult);
67 }
68
69 @Test
70 public void testGetAll(){
71 SqlSession sqlSession = MyBatisUtil.getSqlSession();
72 /
*
73 * 映射sql的标识字符串,
74 * me.gacl.mapping.userMapper是userMapper.xml文件中mapper标签的namespace属性的值,
75 * getAllUsers是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
76 */
77 String statement = “me.gacl.mapping.userMapper.getAllUsers”;//映射sql的标识字符串
78 //执行查询操作,将查询结果自动封装成List返回
79 List lstUsers = sqlSession.selectList(statement);
80 //使用SqlSession执行完SQL之后需要关闭SqlSession
81 sqlSession.close();
82 System.out.println(lstUsers);
83 }
84 }

二、使用MyBatis对表执行CRUD操作——基于注解的实现
  1、定义sql映射的接口
  UserMapperI接口的代码如下:

1 package me.gacl.mapping;
2
3 import java.util.List;
4 import me.gacl.domain.User;
5 import org.apache.ibatis.annotations.Delete;
6 import org.apache.ibatis.annotations.Insert;
7 import org.apache.ibatis.annotations.Select;
8 import org.apache.ibatis.annotations.Update;
9
10 /**
11 * @author gacl
12 * 定义sql映射的接口,使用注解指明方法要执行的SQL
13 */
14 public interface UserMapperI {
15
16 //使用@Insert注解指明add方法要执行的SQL
17 @Insert(“insert into users(name, age) values(#{name}, #{age})”)
18 public int add(User user);
19
20 //使用@Delete注解指明deleteById方法要执行的SQL
21 @Delete(“delete from users where id=#{id}”)
22 public int deleteById(int id);
23
24 //使用@Update注解指明update方法要执行的SQL
25 @Update(“update users set name=#{name},age=#{age} where id=#{id}”)
26 public int update(User user);
27
28 //使用@Select注解指明getById方法要执行的SQL
29 @Select(“select * from users where id=#{id}”)
30 public User getById(int id);
31
32 //使用@Select注解指明getAll方法要执行的SQL
33 @Select(“select * from users”)
34 public List getAll();
35 }

需要说明的是,我们不需要针对UserMapperI接口去编写具体的实现类代码,这个具体的实现类由MyBatis帮我们动态构建出来,我们只需要直接拿来使用即可。
  2、在conf.xml文件中注册这个映射接口

1 <?xml version="1.0" encoding="UTF-8"?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
20
21
22
23
24
25

单元测试类的代码如下:

1 package me.gacl.test;
2
3 import java.util.List;
4 import me.gacl.domain.User;
5 import me.gacl.mapping.UserMapperI;
6 import me.gacl.util.MyBatisUtil;
7 import org.apache.ibatis.session.SqlSession;
8 import org.junit.Test;
9
10 public class TestCRUDByAnnotationMapper {
11
12 @Test
13 public void testAdd(){
14 SqlSession sqlSession = MyBatisUtil.getSqlSession(true);
15 //得到UserMapperI接口的实现类对象,UserMapperI接口的实现类对象由sqlSession.getMapper(UserMapperI.class)动态构建出来
16 UserMapperI mapper = sqlSession.getMapper(UserMapperI.class);
17 User user = new User();
18 user.setName(“用户xdp”);
19 user.setAge(20);
20 int add = mapper.add(user);
21 //使用SqlSession执行完SQL之后需要关闭SqlSession
22 sqlSession.close();
23 System.out.println(add);
24 }
25
26 @Test
27 public void testUpdate(){
28 SqlSession sqlSession = MyBatisUtil.getSqlSession(true);
29 //得到UserMapperI接口的实现类对象,UserMapperI接口的实现类对象由sqlSession.getMapper(UserMapperI.class)动态构建出来
30 UserMapperI mapper = sqlSession.getMapper(UserMapperI.class);
31 User user = new User();
32 user.setId(3);
33 user.setName(“孤傲苍狼_xdp”);
34 user.setAge(26);
35 //执行修改操作
36 int retResult = mapper.update(user);
37 //使用SqlSession执行完SQL之后需要关闭SqlSession
38 sqlSession.close();
39 System.out.println(retResult);
40 }
41
42 @Test
43 public void testDelete(){
44 SqlSession sqlSession = MyBatisUtil.getSqlSession(true);
45 //得到UserMapperI接口的实现类对象,UserMapperI接口的实现类对象由sqlSession.getMapper(UserMapperI.class)动态构建出来
46 UserMapperI mapper = sqlSession.getMapper(UserMapperI.class);
47 //执行删除操作
48 int retResult = mapper.deleteById(7);
49 //使用SqlSession执行完SQL之后需要关闭SqlSession
50 sqlSession.close();
51 System.out.println(retResult);
52 }
53
54 @Test
55 public void testGetUser(){
56 SqlSession sqlSession = MyBatisUtil.getSqlSession();
57 //得到UserMapperI接口的实现类对象,UserMapperI接口的实现类对象由sqlSession.getMapper(UserMapperI.class)动态构建出来
58 UserMapperI mapper = sqlSession.getMapper(UserMapperI.class);
59 //执行查询操作,将查询结果自动封装成User返回
60 User user = mapper.getById(8);
61 //使用SqlSession执行完SQL之后需要关闭SqlSession
62 sqlSession.close();
63 System.out.println(user);
64 }
65
66 @Test
67 public void testGetAll(){
68 SqlSession sqlSession = MyBatisUtil.getSqlSession();
69 //得到UserMapperI接口的实现类对象,UserMapperI接口的实现类对象由sqlSession.getMapper(UserMapperI.class)动态构建出来
70 UserMapperI mapper = sqlSession.getMapper(UserMapperI.class);
71 //执行查询操作,将查询结果自动封装成List返回
72 List lstUsers = mapper.getAll();
73 //使用SqlSession执行完SQL之后需要关闭SqlSession
74 sqlSession.close();
75 System.out.println(lstUsers);
76 }
77 }

用到的MyBatisUtil工具类代码如下:

1 package me.gacl.util;
2
3 import java.io.InputStream;
4
5 import org.apache.ibatis.session.SqlSession;
6 import org.apache.ibatis.session.SqlSessionFactory;
7 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
8
9 public class MyBatisUtil {
10
11 /**
12 * 获取SqlSessionFactory
13 * @return SqlSessionFactory
14 /
15 public static SqlSessionFactory getSqlSessionFactory() {
16 String resource = “conf.xml”;
17 InputStream is = MyBatisUtil.class.getClassLoader().getResourceAsStream(resource);
18 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
19 return factory;
20 }
21
22 /
*
23 * 获取SqlSession
24 * @return SqlSession
25 /
26 public static SqlSession getSqlSession() {
27 return getSqlSessionFactory().openSession();
28 }
29
30 /
*
31 * 获取SqlSession
32 * @param isAutoCommit
33 * true 表示创建的SqlSession对象在执行完SQL之后会自动提交事务
34 * false 表示创建的SqlSession对象在执行完SQL之后不会自动提交事务,这时就需要我们手动调用sqlSession.commit()提交事务
35 * @return SqlSession
36 */
37 public static SqlSession getSqlSession(boolean isAutoCommit) {
38 return getSqlSessionFactory().openSession(isAutoCommit);
39 }
40 }

以上的相关代码是全部测试通过的,关于使用MyBatis对表执行CRUD操作的内容就这么多。
三只为成功找方法,不为失败找借口!
MyBatis学习总结(三)——优化MyBatis配置文件中的配置
一、连接数据库的配置单独放在一个properties文件中
  之前,我们是直接将数据库的连接配置信息写在了MyBatis的conf.xml文件中,如下:

1 <?xml version="1.0" encoding="UTF-8"?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

其实我们完全可以将数据库的连接配置信息写在一个properties文件中,然后在conf.xml文件中引用properties文件,具体做法如下:
  1、在src目录下新建一个db.properties文件,如下图所示:
  
  在db.properties文件编写连接数据库需要使用到的数据库驱动,连接URL地址,用户名,密码,如下:
1 driver=com.mysql.jdbc.Driver
2 url=jdbc:mysql://localhost:3306/mybatis
3 name=root
4 password=XDP
  2、在MyBatis的conf.xml文件中引用db.properties文件,如下:

1 <?xml version="1.0" encoding="UTF-8"?>
2
3
4
5
6
7
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

二、为实体类定义别名,简化sql映射xml文件中的引用
  之前,我们在sql映射xml文件中的引用实体类时,需要写上实体类的全类名(包名+类名),如下:

insert into users(name,age) values(#{name},#{age})   parameterType="me.gacl.domain.User"这里写的实体类User的全类名me.gacl.domain.User,每次都写这么一长串内容挺麻烦的,而我们希望能够简写成下面的形式 insert into users(name,age) values(#{name},#{age})   parameterType="_User"这样写就简单多了,为了达到这种效果,我们需要在conf.xml文件中为实体类="me.gacl.domain.User"定义一个别名为"_User",具体做法如下:   在conf.xml文件中标签中添加如下配置:   这样就可以为me.gacl.domain.User类定义了一个别名为_User,以后_User就代表了me.gacl.domain.User类,这样sql映射xml文件中的凡是需要引用me.gacl.domain.User类的地方都可以使用_User来代替,这就达到了一个简化实体类引用的目的。   除了可以使用这种方式单独为某一个实体类设置别名之外,我们还可以使用如下的方式批量为某个包下的所有实体类设置别名,如下:
<typeAliases>
    <!-- 为实体类me.gacl.domain.User配置一个别名_User -->
    <!-- <typeAlias type="me.gacl.domain.User" alias="_User"/> -->
    <!-- 为me.gacl.domain包下的所有实体类配置别名,MyBatis默认的设置别名的方式就是去除类所在的包后的简单的类名
    比如me.gacl.domain.User这个实体类的别名就会被设置成User
     -->
    <package name="me.gacl.domain"/>
</typeAliases>

就表示为这个包下面的所有实体类设置别名。MyBatis默认的设置别名的方式就是去除类所在的包后的简单的类名,比如me.gacl.domain.User这个实体类的别名就会被设置成User。
只为成功找方法,不为失败找借口!
MyBatis学习总结(四)——解决字段名与实体类属性名不相同的冲突
  在平时的开发中,我们表中的字段名和表对应实体类的属性名称不一定都是完全相同的,下面来演示一下这种情况下的如何解决字段名与实体类属性名不相同的冲突。
一、准备演示需要使用的表和数据

CREATE TABLE orders(
order_id INT PRIMARY KEY AUTO_INCREMENT,
order_no VARCHAR(20),
order_price FLOAT
);
INSERT INTO orders(order_no, order_price) VALUES(‘aaaa’, 23);
INSERT INTO orders(order_no, order_price) VALUES(‘bbbb’, 33);
INSERT INTO orders(order_no, order_price) VALUES(‘cccc’, 22);

二、定义实体类

1 package me.gacl.domain;
2
3 /**
4 * @author gacl
5 * 定义orders表对应的实体类
6 /
7 public class Order {
8 /
*
9 *
10 CREATE TABLE orders(
11 order_id INT PRIMARY KEY AUTO_INCREMENT,
12 order_no VARCHAR(20),
13 order_price FLOAT
14 );
15 */
16
17 //Order实体类中属性名和orders表中的字段名是不一样的
18 private int id; //id===>order_id
19 private String orderNo; //orderNo===>order_no
20 private float price; //price===>order_price
21
22 public int getId() {
23 return id;
24 }
25
26 public void setId(int id) {
27 this.id = id;
28 }
29
30 public String getOrderNo() {
31 return orderNo;
32 }
33
34 public void setOrderNo(String orderNo) {
35 this.orderNo = orderNo;
36 }
37
38 public float getPrice() {
39 return price;
40 }
41
42 public void setPrice(float price) {
43 this.price = price;
44 }
45
46 @Override
47 public String toString() {
48 return “Order [id=” + id + “, orderNo=” + orderNo + “, price=” + price+ “]”;
49 }
50 }

三、编写测试代码
3.1、编写SQL的xml映射文件
  1、创建一个orderMapper.xml文件,orderMapper.xml的内容如下:

1 <?xml version="1.0" encoding="UTF-8" ?>
2
3
6
7
8
12 <select id=“getOrderById” parameterType=“int”
13 resultType=“me.gacl.domain.Order”>
14 select * from orders where order_id=#{id}
15
16
17
21 <select id=“selectOrder” parameterType=“int”
22 resultType=“me.gacl.domain.Order”>
23 select order_id id, order_no orderNo,order_price price from orders where order_id=#{id}
24
25
26
29
30 select * from orders where order_id=#{id}
31
32
33
34
35
36
37
38
39
40
41

2、在conf.xml文件中注册orderMapper.xml映射文件




3.2、编写单元测试代码

1 package me.gacl.test;
2
3 import me.gacl.domain.Order;
4 import me.gacl.util.MyBatisUtil;
5 import org.apache.ibatis.session.SqlSession;
6 import org.junit.Test;
7
8 public class Test2 {
9
10 @Test
11 public void testGetOrderById(){
12 SqlSession sqlSession = MyBatisUtil.getSqlSession();
13 /**
14 * 映射sql的标识字符串,
15 * me.gacl.mapping.orderMapper是orderMapper.xml文件中mapper标签的namespace属性的值,
16 * getOrderById是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
17 /
18 String statement = “me.gacl.mapping.orderMapper.getOrderById”;//映射sql的标识字符串
19 //执行查询操作,将查询结果自动封装成Order对象返回
20 Order order = sqlSession.selectOne(statement,1);//查询orders表中id为1的记录
21 //使用SqlSession执行完SQL之后需要关闭SqlSession
22 sqlSession.close();
23 System.out.println(order);//打印结果:null,也就是没有查询出相应的记录
24 }
25
26 @Test
27 public void testGetOrderById2(){
28 SqlSession sqlSession = MyBatisUtil.getSqlSession();
29 /
*
30 * 映射sql的标识字符串,
31 * me.gacl.mapping.orderMapper是orderMapper.xml文件中mapper标签的namespace属性的值,
32 * selectOrder是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
33 /
34 String statement = “me.gacl.mapping.orderMapper.selectOrder”;//映射sql的标识字符串
35 //执行查询操作,将查询结果自动封装成Order对象返回
36 Order order = sqlSession.selectOne(statement,1);//查询orders表中id为1的记录
37 //使用SqlSession执行完SQL之后需要关闭SqlSession
38 sqlSession.close();
39 System.out.println(order);//打印结果:Order [id=1, orderNo=aaaa, price=23.0]
40 }
41
42 @Test
43 public void testGetOrderById3(){
44 SqlSession sqlSession = MyBatisUtil.getSqlSession();
45 /
*
46 * 映射sql的标识字符串,
47 * me.gacl.mapping.orderMapper是orderMapper.xml文件中mapper标签的namespace属性的值,
48 * selectOrderResultMap是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
49 */
50 String statement = “me.gacl.mapping.orderMapper.selectOrderResultMap”;//映射sql的标识字符串
51 //执行查询操作,将查询结果自动封装成Order对象返回
52 Order order = sqlSession.selectOne(statement,1);//查询orders表中id为1的记录
53 //使用SqlSession执行完SQL之后需要关闭SqlSession
54 sqlSession.close();
55 System.out.println(order);//打印结果:Order [id=1, orderNo=aaaa, price=23.0]
56 }
57 }

执行单元测试的结果:
  1、testGetOrderById方法执行查询后返回一个null。
  2、testGetOrderById2方法和testGetOrderById3方法执行查询后可以正常得到想要的结果。
四、总结
  上面的测试代码演示当实体类中的属性名和表中的字段名不一致时,使用MyBatis进行查询操作时无法查询出相应的结果的问题以及针对问题采用的两种办法:
  解决办法一: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致,这样就可以表的字段名和实体类的属性名一一对应上了,这种方式是通过在sql语句中定义别名来解决字段名和属性名的映射关系的。
  解决办法二: 通过来映射字段名和实体类属性名的一一对应关系。这种方式是使用MyBatis提供的解决方式来解决字段名和属性名的映射关系的。
只为成功找方法,不为失败找借口!
MyBatis学习总结(五)——实现关联表查询
一、一对一关联
 1.1、提出需求
  根据班级id查询班级信息(带老师的信息)
1.2、创建表和数据
  创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关系。

1 CREATE TABLE teacher(
2 t_id INT PRIMARY KEY AUTO_INCREMENT,
3 t_name VARCHAR(20)
4 );
5 CREATE TABLE class(
6 c_id INT PRIMARY KEY AUTO_INCREMENT,
7 c_name VARCHAR(20),
8 teacher_id INT
9 );
10 ALTER TABLE class ADD CONSTRAINT fk_teacher_id FOREIGN KEY (teacher_id) REFERENCES teacher(t_id);
11
12 INSERT INTO teacher(t_name) VALUES(‘teacher1’);
13 INSERT INTO teacher(t_name) VALUES(‘teacher2’);
14
15 INSERT INTO class(c_name, teacher_id) VALUES(‘class_a’, 1);
16 INSERT INTO class(c_name, teacher_id) VALUES(‘class_b’, 2);

表之间的关系如下:
  
1.3、定义实体类
  1、Teacher类,Teacher类是teacher表对应的实体类。

1 package me.gacl.domain;
2
3 /**
4 * @author gacl
5 * 定义teacher表对应的实体类
6 */
7 public class Teacher {
8
9 //定义实体类的属性,与teacher表中的字段对应
10 private int id; //id===>t_id
11 private String name; //name===>t_name
12
13 public int getId() {
14 return id;
15 }
16
17 public void setId(int id) {
18 this.id = id;
19 }
20
21 public String getName() {
22 return name;
23 }
24
25 public void setName(String name) {
26 this.name = name;
27 }
28
29 @Override
30 public String toString() {
31 return “Teacher [id=” + id + “, name=” + name + “]”;
32 }
33 }

2、Classes类,Classes类是class表对应的实体类

1 package me.gacl.domain;
2
3 /**
4 * @author gacl
5 * 定义class表对应的实体类
6 /
7 public class Classes {
8
9 //定义实体类的属性,与class表中的字段对应
10 private int id; //id===>c_id
11 private String name; //name===>c_name
12
13 /
*
14 * class表中有一个teacher_id字段,所以在Classes类中定义一个teacher属性,
15 * 用于维护teacher和class之间的一对一关系,通过这个teacher属性就可以知道这个班级是由哪个老师负责的
16 */
17 private Teacher teacher;
18
19 public int getId() {
20 return id;
21 }
22
23 public void setId(int id) {
24 this.id = id;
25 }
26
27 public String getName() {
28 return name;
29 }
30
31 public void setName(String name) {
32 this.name = name;
33 }
34
35 public Teacher getTeacher() {
36 return teacher;
37 }
38
39 public void setTeacher(Teacher teacher) {
40 this.teacher = teacher;
41 }
42
43 @Override
44 public String toString() {
45 return “Classes [id=” + id + “, name=” + name + “, teacher=” + teacher+ “]”;
46 }
47 }

1.4、定义sql映射文件classMapper.xml

1 <?xml version="1.0" encoding="UTF-8" ?>
2
3
6
7
8
17
18
23
24 select * from class c, teacher t where c.teacher_id=t.t_id and c.c_id=#{id}
25
26
27
28
29
30
31
32
33
34
35
36
41
42 select * from class where c_id=#{id}
43
44
45
46
47
48
49
50
51
52 SELECT t_id id, t_name name FROM teacher WHERE t_id=#{id}
53
54
55

在conf.xml文件中注册classMapper.xml




1.5、编写单元测试代码

1 package me.gacl.test;
2
3 import me.gacl.domain.Classes;
4 import me.gacl.util.MyBatisUtil;
5 import org.apache.ibatis.session.SqlSession;
6 import org.junit.Test;
7
8 public class Test3 {
9
10 @Test
11 public void testGetClass(){
12 SqlSession sqlSession = MyBatisUtil.getSqlSession();
13 /**
14 * 映射sql的标识字符串,
15 * me.gacl.mapping.classMapper是classMapper.xml文件中mapper标签的namespace属性的值,
16 * getClass是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
17 /
18 String statement = “me.gacl.mapping.classMapper.getClass”;//映射sql的标识字符串
19 //执行查询操作,将查询结果自动封装成Classes对象返回
20 Classes clazz = sqlSession.selectOne(statement,1);//查询class表中id为1的记录
21 //使用SqlSession执行完SQL之后需要关闭SqlSession
22 sqlSession.close();
23 System.out.println(clazz);//打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1]]
24 }
25
26 @Test
27 public void testGetClass2(){
28 SqlSession sqlSession = MyBatisUtil.getSqlSession();
29 /
*
30 * 映射sql的标识字符串,
31 * me.gacl.mapping.classMapper是classMapper.xml文件中mapper标签的namespace属性的值,
32 * getClass2是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
33 */
34 String statement = “me.gacl.mapping.classMapper.getClass2”;//映射sql的标识字符串
35 //执行查询操作,将查询结果自动封装成Classes对象返回
36 Classes clazz = sqlSession.selectOne(statement,1);//查询class表中id为1的记录
37 //使用SqlSession执行完SQL之后需要关闭SqlSession
38 sqlSession.close();
39 System.out.println(clazz);//打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1]]
40 }
41 }

1.6、MyBatis一对一关联查询总结
  MyBatis中使用association标签来解决一对一的关联查询,association标签可用的属性如下:
property:对象属性的名称
javaType:对象属性的类型
column:所对应的外键字段名称
select:使用另一个查询封装的结果
二、一对多关联
2.1、提出需求
  根据classId查询对应的班级信息,包括学生,老师
2.2、创建表和数据
  在上面的一对一关联查询演示中,我们已经创建了班级表和教师表,因此这里再创建一张学生表

CREATE TABLE student(
s_id INT PRIMARY KEY AUTO_INCREMENT,
s_name VARCHAR(20),
class_id INT
);
INSERT INTO student(s_name, class_id) VALUES(‘student_A’, 1);
INSERT INTO student(s_name, class_id) VALUES(‘student_B’, 1);
INSERT INTO student(s_name, class_id) VALUES(‘student_C’, 1);
INSERT INTO student(s_name, class_id) VALUES(‘student_D’, 2);
INSERT INTO student(s_name, class_id) VALUES(‘student_E’, 2);
INSERT INTO student(s_name, class_id) VALUES(‘student_F’, 2);

2.3、定义实体类
  1、Student类

1 package me.gacl.domain;
2
3 /**
4 * @author gacl
5 * 定义student表所对应的实体类
6 */
7 public class Student {
8
9 //定义属性,和student表中的字段对应
10 private int id; //id===>s_id
11 private String name; //name===>s_name
12
13 public int getId() {
14 return id;
15 }
16
17 public void setId(int id) {
18 this.id = id;
19 }
20
21 public String getName() {
22 return name;
23 }
24
25 public void setName(String name) {
26 this.name = name;
27 }
28
29 @Override
30 public String toString() {
31 return “Student [id=” + id + “, name=” + name + “]”;
32 }
33 }

2、修改Classes类,添加一个List students属性,使用一个List集合属性表示班级拥有的学生,如下:

1 package me.gacl.domain;
2
3 import java.util.List;
4
5 /**
6 * @author gacl
7 * 定义class表对应的实体类
8 /
9 public class Classes {
10
11 //定义实体类的属性,与class表中的字段对应
12 private int id; //id===>c_id
13 private String name; //name===>c_name
14
15 /
*
16 * class表中有一个teacher_id字段,所以在Classes类中定义一个teacher属性,
17 * 用于维护teacher和class之间的一对一关系,通过这个teacher属性就可以知道这个班级是由哪个老师负责的
18 */
19 private Teacher teacher;
20 //使用一个List集合属性表示班级拥有的学生
21 private List students;
22
23 public int getId() {
24 return id;
25 }
26
27 public void setId(int id) {
28 this.id = id;
29 }
30
31 public String getName() {
32 return name;
33 }
34
35 public void setName(String name) {
36 this.name = name;
37 }
38
39 public Teacher getTeacher() {
40 return teacher;
41 }
42
43 public void setTeacher(Teacher teacher) {
44 this.teacher = teacher;
45 }
46
47 public List getStudents() {
48 return students;
49 }
50
51 public void setStudents(List students) {
52 this.students = students;
53 }
54
55 @Override
56 public String toString() {
57 return “Classes [id=” + id + “, name=” + name + “, teacher=” + teacher
58 + “, students=” + students + “]”;
59 }
60 }

2.4、修改sql映射文件classMapper.xml
  添加如下的SQL映射信息

1
4
8
9 select * from class c, teacher t,student s where c.teacher_id=t.t_id and c.C_id=s.class_id and c.c_id=#{id}
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
31
32 select * from class where c_id=#{id}
33
34
35
36
37
38
39
40
41
42 SELECT t_id id, t_name name FROM teacher WHERE t_id=#{id}
43
44
45
46 SELECT s_id id, s_name name FROM student WHERE class_id=#{id}
47

2.5、编写单元测试代码

1 package me.gacl.test;
2
3 import me.gacl.domain.Classes;
4 import me.gacl.util.MyBatisUtil;
5 import org.apache.ibatis.session.SqlSession;
6 import org.junit.Test;
7
8 public class Test4 {
9
10 @Test
11 public void testGetClass3(){
12 SqlSession sqlSession = MyBatisUtil.getSqlSession();
13 /**
14 * 映射sql的标识字符串,
15 * me.gacl.mapping.classMapper是classMapper.xml文件中mapper标签的namespace属性的值,
16 * getClass3是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
17 /
18 String statement = “me.gacl.mapping.classMapper.getClass3”;//映射sql的标识字符串
19 //执行查询操作,将查询结果自动封装成Classes对象返回
20 Classes clazz = sqlSession.selectOne(statement,1);//查询class表中id为1的记录
21 //使用SqlSession执行完SQL之后需要关闭SqlSession
22 sqlSession.close();
23 //打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1], students=[Student [id=1, name=student_A], Student [id=2, name=student_B], Student [id=3, name=student_C]]]
24 System.out.println(clazz);
25 }
26
27 @Test
28 public void testGetClass4(){
29 SqlSession sqlSession = MyBatisUtil.getSqlSession();
30 /
*
31 * 映射sql的标识字符串,
32 * me.gacl.mapping.classMapper是classMapper.xml文件中mapper标签的namespace属性的值,
33 * getClass4是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
34 */
35 String statement = “me.gacl.mapping.classMapper.getClass4”;//映射sql的标识字符串
36 //执行查询操作,将查询结果自动封装成Classes对象返回
37 Classes clazz = sqlSession.selectOne(statement,1);//查询class表中id为1的记录
38 //使用SqlSession执行完SQL之后需要关闭SqlSession
39 sqlSession.close();
40 //打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1], students=[Student [id=1, name=student_A], Student [id=2, name=student_B], Student [id=3, name=student_C]]]
41 System.out.println(clazz);
42 }
43 }

2.6、MyBatis一对多关联查询总结
  MyBatis中使用collection标签来解决一对多的关联查询,ofType属性指定集合中元素的对象类型。
只为成功找方法,不为失败找借口!
MyBatis学习总结(六)——调用存储过程
一、提出需求
  查询得到男性或女性的数量, 如果传入的是0就女性否则是男性
二、准备数据库表和存储过程

1 create table p_user(
2 id int primary key auto_increment,
3 name varchar(10),
4 sex char(2)
5 );
6
7 insert into p_user(name,sex) values(‘A’,“男”);
8 insert into p_user(name,sex) values(‘B’,“女”);
9 insert into p_user(name,sex) values(‘C’,“男”);
10
11 – 创建存储过程(查询得到男性或女性的数量, 如果传入的是0就女性否则是男性)
IN 是入参 OUT是出参
12 DELIMITER ( ( 意思是结束符)
13 CREATE PROCEDURE mybatis.ges_user_count(IN sex_id INT, OUT user_count INT)
14 BEGIN
15 IF sex_id=0 THEN
16 SELECT COUNT() FROM mybatis.p_user WHERE p_user.sex=‘女’ INTO user_count;
17 ELSE
18 SELECT COUNT(
) FROM mybatis.p_user WHERE p_user.sex=‘男’ INTO user_count;
19 END IF;
20 END
21 $
22
23 – 调用存储过程
24 DELIMITER ;
25 SET @user_count = 0;
26 CALL mybatis.ges_user_count(1, @user_count);
27 SELECT @user_count;

三、编辑userMapper.xml
  编辑userMapper.xml文件,添加如下的配置项

1
4
5 CALL mybatis.ges_user_count(?,?)
6
7
8
12
13
14
15

四、编写单元测试代码

1 package me.gacl.test;
2
3 import java.util.HashMap;
4 import java.util.List;
5 import java.util.Map;
6
7 import me.gacl.custom.model.ConditionUser;
8 import me.gacl.domain.User;
9 import me.gacl.util.MyBatisUtil;
10 import org.apache.ibatis.session.SqlSession;
11 import org.junit.Test;
12
13 /**
14 * @author gacl
15 * 测试调用存储过程
16 /
17 public class Test6 {
18
19 @Test
20 public void testGetUserCount(){
21 SqlSession sqlSession = MyBatisUtil.getSqlSession();
22 /
*
23 * 映射sql的标识字符串,
24 * me.gacl.mapping.userMapper是userMapper.xml文件中mapper标签的namespace属性的值,
25 * getUserCount是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
26 */
27 String statement = “me.gacl.mapping.userMapper.getUserCount”;//映射sql的标识字符串
28 Map<String, Integer> parameterMap = new HashMap<String, Integer>();
29 parameterMap.put(“sexid”, 1);
30 parameterMap.put(“usercount”, -1);
31 sqlSession.selectOne(statement, parameterMap);
32 Integer result = parameterMap.get(“usercount”);
33 System.out.println(result);
34 sqlSession.close();
35 }
36 }

只为成功找方法,不为失败找借口!
MyBatis学习总结(七)——Mybatis缓存
一、MyBatis缓存介绍
  正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持
1.一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
  2. 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。
  3. 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。
1.1、Mybatis一级缓存测试

1 package me.gacl.test;
2
3 import me.gacl.domain.User;
4 import me.gacl.util.MyBatisUtil;
5 import org.apache.ibatis.session.SqlSession;
6 import org.junit.Test;
7
8 /**
9 * @author gacl
10 * 测试一级缓存
11 /
12 public class TestOneLevelCache {
13
14 /

15 * 一级缓存: 也就Session级的缓存(默认开启)
16 /
17 @Test
18 public void testCache1() {
19 SqlSession session = MyBatisUtil.getSqlSession();
20 String statement = “me.gacl.mapping.userMapper.getUser”;
21 User user = session.selectOne(statement, 1);
22 System.out.println(user);
23
24 /

25 * 一级缓存默认就会被使用
26 /
27 user = session.selectOne(statement, 1);
28 System.out.println(user);
29 session.close();
30 /

31 1. 必须是同一个Session,如果session对象已经close()过了就不可能用了
32 /
33 session = MyBatisUtil.getSqlSession();
34 user = session.selectOne(statement, 1);
35 System.out.println(user);
36
37 /

38 2. 查询条件是一样的
39 /
40 user = session.selectOne(statement, 2);
41 System.out.println(user);
42
43 /

44 3. 没有执行过session.clearCache()清理缓存
45 /
46 //session.clearCache();
47 user = session.selectOne(statement, 2);
48 System.out.println(user);
49
50 /

51 4. 没有执行过增删改的操作(这些操作都会清理缓存)
52 */
53 session.update(“me.gacl.mapping.userMapper.updateUser”,
54 new User(2, “user”, 23));
55 user = session.selectOne(statement, 2);
56 System.out.println(user);
57
58 }
59 }

1.2、Mybatis二级缓存测试
  1、开启二级缓存,在userMapper.xml文件中添加如下配置

  2、测试二级缓存

1 package me.gacl.test;
2
3 import me.gacl.domain.User;
4 import me.gacl.util.MyBatisUtil;
5 import org.apache.ibatis.session.SqlSession;
6 import org.apache.ibatis.session.SqlSessionFactory;
7 import org.junit.Test;
8
9 /**
10 * @author gacl
11 * 测试二级缓存
12 /
13 public class TestTwoLevelCache {
14
15 /

16 * 测试二级缓存
17 * 使用两个不同的SqlSession对象去执行相同查询条件的查询,第二次查询时不会再发送SQL语句,而是直接从缓存中取出数据
18 */
19 @Test
20 public void testCache2() {
21 String statement = “me.gacl.mapping.userMapper.getUser”;
22 SqlSessionFactory factory = MyBatisUtil.getSqlSessionFactory();
23 //开启两个不同的SqlSession
24 SqlSession session1 = factory.openSession();
25 SqlSession session2 = factory.openSession();
26 //使用二级缓存时,User类必须实现一个Serializable接口===> User implements Serializable
27 User user = session1.selectOne(statement, 1);
28 session1.commit();//不懂为啥,这个地方一定要提交事务之后二级缓存才会起作用
29 System.out.println(“user=”+user);
30
31 //由于使用的是两个不同的SqlSession对象,所以即使查询条件相同,一级缓存也不会开启使用
32 user = session2.selectOne(statement, 1);
33 //session2.commit();
34 System.out.println(“user2=”+user);
35 }
36 }

1.3、二级缓存补充说明
  1. 映射语句文件中的所有select语句将会被缓存。
  2. 映射语句文件中的所有insert,update和delete语句会刷新缓存。
  3. 缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回。
  4. 缓存会根据指定的时间间隔来刷新。
  5. 缓存会存储1024个对象
cache标签常用属性:
<cache
eviction=“FIFO”
flushInterval=“60000”
size=“512”
readOnly=“true”/>
 
孤傲苍狼
只为成功找方法,不为失败找借口!
MyBatis学习总结(八)——Mybatis3.x与Spring4.x整合
一、搭建开发环境
1.1、使用Maven创建Web项目
  执行如下命令:
mvn archetype:create -DgroupId=me.gacl -DartifactId=spring4-mybatis3 -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
  如下图所示:
  
  创建好的项目如下:
  
  编辑pom.xml文件

1 <project xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance
2 xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd”>
3 4.0.0
4 me.gacl
5 spring4-mybatis3
6 war
7 1.0-SNAPSHOT
8 spring4-mybatis3 Maven Webapp
9 http://maven.apache.org
10
11
12 junit
13 junit
14 3.8.1
15 test
16
17
18
19 spring4-mybatis3
20
21

修改 spring4-mybatis3 Maven Webapp 部分,把" Maven Webapp"这部分包含空格的内容去掉,否则Maven在编译项目时会因为空格的原因导致一些莫名其妙的错误出现,修改成: spring4-mybatis3 。
  另外,把以下内容删掉:
1
2 junit
3 junit
4 3.8.1
5 test
6
  这部分是junit的jar包依赖信息,这个版本太低了,我们不使用这个Junit测试版本,修改过后的pom.xml内容如下:

1 <project xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance
2 xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd”>
3 4.0.0
4 me.gacl
5 spring4-mybatis3
6 war
7 1.0-SNAPSHOT
8 spring4-mybatis3
9 http://maven.apache.org
10
11
12
13
14 spring4-mybatis3
15
16

1.2、将创建好的项目导入MyEclipse中
  具体操作步骤如下图所示:
  
  
  
  
  
  手动创建【src/main/java】、【src/test/resources】、【src/test/java】这三个source folder,如下图所示:
  
  到此,项目搭建的工作就算是全部完成了。
二、创建数据库和表(针对MySQL)
SQL脚本如下:

Create DATABASE spring4_mybatis3;
USE spring4_mybatis3;

DROP TABLE IF EXISTS t_user;
CREATE TABLE t_user (
user_id char(32) NOT NULL,
user_name varchar(30) DEFAULT NULL,
user_birthday date DEFAULT NULL,
user_salary double DEFAULT NULL,
PRIMARY KEY (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

创建好的数据库和表如下:
  
三、使用generator工具生成代码
  在网上找到了一个generator工具可以根据创建好的数据库表生成MyBatis的表对应的实体类,SQL映射文件和dao,找到generator工具根目录下的generator.xml文件,这个文件是用来配置代码生成规则的,如下图所示:
  
  编辑generator.xml文件,内容如下:

1 <?xml version="1.0" encoding="UTF-8"?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32


33
34

打开命令行窗口,切换到生成工具的根目录下,执行如下命令:
java -jar mybatis-generator-core-1.3.2.jar -configfile generator.xml -overwrite
  如下图所示:
  
  刚才我们在generator.xml文件中配置将生成的代码和SQL映射文件放到"C:\Users\gacl\spring4-mybatis3\src\main\java"这个目录下,这个目录就是我们的spring4-mybatis3项目所在目录,我们刷新一下src/main/java目录,就可以看到生成的代码和映射文件了,如下图所示:
  
  生成的代码和映射文件一行都不用改,可以直接应用到项目当中。下面我们看一眼由generator工具生成的代码和映射文件:
  1、生成的dao类

1 package me.gacl.dao;
2
3 import me.gacl.domain.User;
4
5 public interface UserMapper {
6 int deleteByPrimaryKey(String userId);
7
8 int insert(User record);
9
10 int insertSelective(User record);
11
12 User selectByPrimaryKey(String userId);
13
14 int updateByPrimaryKeySelective(User record);
15
16 int updateByPrimaryKey(User record);
17 }

生成的UserMapper是一个接口,里面定义了一些操作t_user表的增删改查方法。
2、生成的实体类

1 package me.gacl.domain;
2
3 import java.util.Date;
4
5 public class User {
6 private String userId;
7
8 private String userName;
9
10 private Date userBirthday;
11
12 private Double userSalary;
13
14 public String getUserId() {
15 return userId;
16 }
17
18 public void setUserId(String userId) {
19 this.userId = userId == null ? null : userId.trim();
20 }
21
22 public String getUserName() {
23 return userName;
24 }
25
26 public void setUserName(String userName) {
27 this.userName = userName == null ? null : userName.trim();
28 }
29
30 public Date getUserBirthday() {
31 return userBirthday;
32 }
33
34 public void setUserBirthday(Date userBirthday) {
35 this.userBirthday = userBirthday;
36 }
37
38 public Double getUserSalary() {
39 return userSalary;
40 }
41
42 public void setUserSalary(Double userSalary) {
43 this.userSalary = userSalary;
44 }
45 }

User类是t_user表的对应的实体类,User类中定义的属性和t_user表中的字段一一对应。
  3、生成的SQL映射文件

1 <?xml version="1.0" encoding="UTF-8" ?>
2
3
4
5
6
7
8
9
10
11 user_id, user_name, user_birthday, user_salary
12
13
14 select
15
16 from t_user
17 where user_id = #{userId,jdbcType=CHAR}
18
19
20 delete from t_user
21 where user_id = #{userId,jdbcType=CHAR}
22
23
24 insert into t_user (user_id, user_name, user_birthday,
25 user_salary)
26 values (#{userId,jdbcType=CHAR}, #{userName,jdbcType=VARCHAR}, #{userBirthday,jdbcType=DATE},
27 #{userSalary,jdbcType=DOUBLE})
28
29
30 insert into t_user
31
32
33 user_id,
34
35
36 user_name,
37
38
39 user_birthday,
40
41
42 user_salary,
43
44
45
46
47 #{userId,jdbcType=CHAR},
48
49
50 #{userName,jdbcType=VARCHAR},
51
52
53 #{userBirthday,jdbcType=DATE},
54
55
56 #{userSalary,jdbcType=DOUBLE},
57
58
59
60
61 update t_user
62
63
64 user_name = #{userName,jdbcType=VARCHAR},
65
66
67 user_birthday = #{userBirthday,jdbcType=DATE},
68
69
70 user_salary = #{userSalary,jdbcType=DOUBLE},
71
72
73 where user_id = #{userId,jdbcType=CHAR}
74
75
76 update t_user
77 set user_name = #{userName,jdbcType=VARCHAR},
78 user_birthday = #{userBirthday,jdbcType=DATE},
79 user_salary = #{userSalary,jdbcType=DOUBLE}
80 where user_id = #{userId,jdbcType=CHAR}
81
82

UserMapper.xml这个文件的内容是编写操作t_user表的SQL语句,重点说一下UserMapper.xml配置中需要注意的几个小细节问题:
  1、UserMapper.xml的标签的namespace必须是UserMapper接口的全类名,既
  2、UserMapper.xml的定义操作数据库的这些标签的id属性的值必须和UserMapper接口定义的方法名一致,如下图所示:
  
  之所以有上述说的这两点要求,就是为了能够让MyBatis能够根据UserMapper接口和UserMapper.xml文件去自动实现UserMapper接口中定义的相关方法,这样我们就不再需要针对UserMapper接口去编写具体的实现代码了。
四、Spring与MyBatis整合
  首先我们要在项目中加入我们需要的相关jar包,我们可以到Maven的中央仓库:http://search.maven.org/ 找到我们要的相关jar包,如下图所示:
  
  我们只需要在搜索框中输入要找的jar包的名称,点击【SEARCH】按钮,就可以找到我们要的jar包了。
4.1、添加Spring与Mybatis的相关jar包
  1、添加spring-core,输入spring-core关键字进行查找,如下图所示:
  
  找到关于spring-core的依赖描述信息,如下图所示:
  
  将

org.springframework
spring-core
4.1.4.RELEASE

  复制到项目的pom.xml文件中,如下所示:


4.0.0
me.gacl
spring4-mybatis3
war
1.0-SNAPSHOT
spring4-mybatis3
http://maven.apache.org



org.springframework
spring-core
4.1.4.RELEASE

</dependencies>
<build>
    <finalName>spring4-mybatis3</finalName>
</build>

这样Maven就会自动帮我们从Maven的中央仓库中下载spring-core这个jar包到我们的本地仓库,然后将spring-core这个jar包以及它的相关依赖包加入到我们的项目当中,如下所示:
  
  spring4.x与mybatis3.x所需要的相关jar包都可以采用上述所说的方式进行查找,然后添加到项目当中,添加完spring4.x与mybatis3.x相关jar包后,pom.xml文件内容最终如下:

1 <project xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance
2 xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd”>
3 4.0.0
4 me.gacl
5 spring4-mybatis3
6 war
7 1.0-SNAPSHOT
8 spring4-mybatis3
9 http://maven.apache.org
10
11
12
13 org.springframework
14 spring-core
15 4.1.4.RELEASE
16
17
18
19 org.springframework
20 spring-context
21 4.1.4.RELEASE
22
23
24
25 org.springframework
26 spring-tx
27 4.1.4.RELEASE
28
29
30
31 org.springframework
32 spring-jdbc
33 4.1.4.RELEASE
34
35
36
37 org.springframework
38 spring-test
39 4.1.4.RELEASE
40
41
42
43 org.springframework
44 spring-web
45 4.1.4.RELEASE
46
47
48
49 org.aspectj
50 aspectjweaver
51 1.8.5
52
53
54
55 org.mybatis
56 mybatis
57 3.2.8
58
59
60
61 org.mybatis
62 mybatis-spring
63 1.2.2
64
65
66
67 javax.servlet
68 javax.servlet-api
69 3.0.1
70
71
72 javax.servlet.jsp
73 javax.servlet.jsp-api
74 2.3.2-b01
75
76
77
78 javax.servlet
79 jstl
80 1.2
81
82
83
84 mysql
85 mysql-connector-java
86 5.1.34
87
88
89
90 com.alibaba
91 druid
92 1.0.12
93
94
95
96 junit
97 junit
98 4.12
99 test
100
101
102
103 spring4-mybatis3
104
105

4.2、编写相关配置文件
  1、dbconfig.properties
  在src/main/resources目录下创建一个dbconfig.properties文件,用于编写连接MySQL数据库的相关信息,dbconfig.properties的内容如下:
driverClassName=com.mysql.jdbc.Driver
validationQuery=SELECT 1
jdbc_url=jdbc:mysql://localhost:3306/spring4_mybatis3?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_username=root
jdbc_password=XDP
  2、spring.xml(spring框架的配置文件)
  在src/main/resources目录下创建一个spring.xml文件,spring.xml文件就是针对Spring框架编写的核心配置文件,spring.xml的内容如下:

<?xml version="1.0" encoding="UTF-8"?>

<!-- 引入dbconfig.properties属性文件 -->
<context:property-placeholder location="classpath:dbconfig.properties" />
<!-- 自动扫描(自动注入),扫描me.gacl.service这个包以及它的子包的所有使用@Service注解标注的类 -->
<context:component-scan base-package="me.gacl.service" />

我们的spring.xml文件的配置非常简单,就两个配置。
  3、spring-mybatis.xml(spring与mybatis整合的配置文件)
  在src/main/resources目录下创建一个spring-mybatis.xml文件,spring-mybatis.xml文件就是针对Spring框架与Mybatis框架整合编写的配置文件,spring-mybatis.xml的内容如下:

<?xml version="1.0" encoding="UTF-8"?>

<!-- JNDI方式配置数据源 -->
<!-- <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="${jndiName}"></property> </bean> -->
<!-- ========================================配置数据源========================================= -->
<!-- 配置数据源,使用的是alibaba的Druid(德鲁伊)数据源 -->
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    <property name="url" value="${jdbc_url}" />
    <property name="username" value="${jdbc_username}" />
    <property name="password" value="${jdbc_password}" />
    <!-- 初始化连接大小 -->
    <property name="initialSize" value="0" />
    <!-- 连接池最大使用连接数量 -->
    <property name="maxActive" value="20" />
    <!-- 连接池最大空闲 -->
    <property name="maxIdle" value="20" />
    <!-- 连接池最小空闲 -->
    <property name="minIdle" value="0" />
    <!-- 获取连接最大等待时间 -->
    <property name="maxWait" value="60000" />
    <!-- 
    <property name="poolPreparedStatements" value="true" /> 
    <property name="maxPoolPreparedStatementPerConnectionSize" value="33" /> 
    -->
    <property name="validationQuery" value="${validationQuery}" />
    <property name="testOnBorrow" value="false" />
    <property name="testOnReturn" value="false" />
    <property name="testWhileIdle" value="true" />
    <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
    <property name="timeBetweenEvictionRunsMillis" value="60000" />
    <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
    <property name="minEvictableIdleTimeMillis" value="25200000" />
    <!-- 打开removeAbandoned功能 -->
    <property name="removeAbandoned" value="true" />
    <!-- 1800秒,也就是30分钟 -->
    <property name="removeAbandonedTimeout" value="1800" />
    <!-- 关闭abanded连接时输出错误日志 -->
    <property name="logAbandoned" value="true" />
    <!-- 监控数据库 -->
    <!-- <property name="filters" value="stat" /> -->
    <property name="filters" value="mergeStat" />
</bean>

<!-- ========================================分隔线========================================= -->

<!-- ========================================针对myBatis的配置项============================== -->
<!-- 配置sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 实例化sqlSessionFactory时需要使用上述配置好的数据源以及SQL映射文件 -->
    <property name="dataSource" ref="dataSource" />
    <!-- 自动扫描me/gacl/mapping/目录下的所有SQL映射的xml文件, 省掉Configuration.xml里的手工配置
    value="classpath:me/gacl/mapping/*.xml"指的是classpath(类路径)下me.gacl.mapping包中的所有xml文件
    UserMapper.xml位于me.gacl.mapping包下,这样UserMapper.xml就可以被自动扫描
     -->
    <property name="mapperLocations" value="classpath:me/gacl/mapping/*.xml" />
</bean>
<!-- 配置扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!-- 扫描me.gacl.dao这个包以及它的子包下的所有映射接口类 -->
    <property name="basePackage" value="me.gacl.dao" />
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>

<!-- ========================================分隔线========================================= -->
<!-- 配置Spring的事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- 注解方式配置事物 -->
<!-- <tx:annotation-driven transaction-manager="transactionManager" /> -->

<!-- 拦截器方式配置事物 -->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="add*" propagation="REQUIRED" />
        <tx:method name="append*" propagation="REQUIRED" />
        <tx:method name="insert*" propagation="REQUIRED" />
        <tx:method name="save*" propagation="REQUIRED" />
        <tx:method name="update*" propagation="REQUIRED" />
        <tx:method name="modify*" propagation="REQUIRED" />
        <tx:method name="edit*" propagation="REQUIRED" />
        <tx:method name="delete*" propagation="REQUIRED" />
        <tx:method name="remove*" propagation="REQUIRED" />
        <tx:method name="repair" propagation="REQUIRED" />
        <tx:method name="delAndRepair" propagation="REQUIRED" />

        <tx:method name="get*" propagation="SUPPORTS" />
        <tx:method name="find*" propagation="SUPPORTS" />
        <tx:method name="load*" propagation="SUPPORTS" />
        <tx:method name="search*" propagation="SUPPORTS" />
        <tx:method name="datagrid*" propagation="SUPPORTS" />

        <tx:method name="*" propagation="SUPPORTS" />
    </tx:attributes>
</tx:advice>
<aop:config>
    <aop:pointcut id="transactionPointcut" expression="execution(* me.gacl.service..*Impl.*(..))" />
    <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
</aop:config>


<!-- 配置druid监控spring jdbc -->
<bean id="druid-stat-interceptor" class="com.alibaba.druid.support.spring.stat.DruidStatInterceptor">
</bean>
<bean id="druid-stat-pointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut" scope="prototype">
    <property name="patterns">
        <list>
            <value>me.gacl.service.*</value>
        </list>
    </property>
</bean>
<aop:config>
    <aop:advisor advice-ref="druid-stat-interceptor" pointcut-ref="druid-stat-pointcut" />
</aop:config>

到此,相关的配置文件算是编写完成了,如下图所示:
  
4.3、进行单元测试
  经过以上两个步骤,spring4与mybatis3的整合算是全部完成了。接下来我们要做的工作就算进行单元测试,测试一下spring4与mybatis3的整合是否成功。
  1、在src/main/java目录下创建一个me.gacl.service包,然后在me.gacl.service包创建一个UserServiceI接口,如下所示:

1 package me.gacl.service;
2
3 import me.gacl.domain.User;
4
5 public interface UserServiceI {
6
7 /**
8 * 添加用户
9 * @param user
10 /
11 void addUser(User user);
12
13 /
*
14 * 根据用户id获取用户
15 * @param userId
16 * @return
17 */
18 User getUserById(String userId);
19 }

2、在src/main/java目录下创建一个me.gacl.service.impl包,然后在me.gacl.service.impl包创建一个针对UserServiceI接口的实现类:UserServiceImpl,如下所示:

1 package me.gacl.service.impl;
2
3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.stereotype.Service;
5 import me.gacl.dao.UserMapper;
6 import me.gacl.domain.User;
7 import me.gacl.service.UserServiceI;
8
9 /**
10 * @author gacl
11 * 使用@Service注解将UserServiceImpl类标注为一个service
12 * service的id是userService
13 /
14 @Service(“userService”)
15 public class UserServiceImpl implements UserServiceI {
16
17 /
*
18 * 使用@Autowired注解标注userMapper变量,
19 * 当需要使用UserMapper时,Spring就会自动注入UserMapper
20 */
21 @Autowired
22 private UserMapper userMapper;//注入dao
23
24 @Override
25 public void addUser(User user) {
26 userMapper.insert(user);
27 }
28
29 @Override
30 public User getUserById(String userId) {
31 return userMapper.selectByPrimaryKey(userId);
32 }
33 }

创建好的两个类如下所示:
  
  3、在src/test/java目录下编写单元测试类,新建一个me.gacl.test包,然后在这个包下创建一个MyBatisTest类,代码如下:

1 package me.gacl.test;
2
3 import java.util.Date;
4 import java.util.UUID;
5 import me.gacl.domain.User;
6 import me.gacl.service.UserServiceI;
7 //import me.gacl.service.UserServiceI;
8 import org.junit.Before;
9 import org.junit.Test;
10 import org.springframework.context.ApplicationContext;
11 import org.springframework.context.support.ClassPathXmlApplicationContext;
12
13 public class MyBatisTest {
14
15 private UserServiceI userService;
16
17 /**
18 * 这个before方法在所有的测试方法之前执行,并且只执行一次
19 * 所有做Junit单元测试时一些初始化工作可以在这个方法里面进行
20 * 比如在before方法里面初始化ApplicationContext和userService
21 */
22 @Before
23 public void before(){
24 //使用"spring.xml"和"spring-mybatis.xml"这两个配置文件创建Spring上下文
25 ApplicationContext ac = new ClassPathXmlApplicationContext(new String[]{“spring.xml”,“spring-mybatis.xml”});
26 //从Spring容器中根据bean的id取出我们要使用的userService对象
27 userService = (UserServiceI) ac.getBean(“userService”);
28 }
29
30 @Test
31 public void testAddUser(){
32 //ApplicationContext ac = new ClassPathXmlApplicationContext(new String[]{“spring.xml”,“spring-mybatis.xml”});
33 //UserServiceI userService = (UserServiceI) ac.getBean(“userService”);
34 User user = new User();
35 user.setUserId(UUID.randomUUID().toString().replaceAll("-", “”));
36 user.setUserName(“白虎神皇xdp”);
37 user.setUserBirthday(new Date());
38 user.setUserSalary(10000D);
39 userService.addUser(user);
40 }
41
42 }

执行单元测试代码,这时候会报如下错误:
   
  错误提示是说没有找到"me.gacl.test.MyBatisTest"这个类,这是因为我们没有使用maven编译项目中的类的缘故。
  下面我们使用Maven编译项目,选中项目的pom.xml文件→【Debug As】→【maven install】,如下所示:
  
编译结果如下:
  
  在这里说一下我执行Maven install之后遇到的问题,第一次执行Maven install命令时,就出现了如下一堆乱七八糟的错误:
  
  后来我把项目删掉,再重新导入项目,然后再执行Clean项目操作之后,如下图所示:
  
  再执行Maven install操作又可以正常编译通过了,这让我郁闷了好久,这应该不是我项目配置的原因,而是Maven的原因,具体也不知道为啥会这样。反正这算是一种解决办法吧,如果遇到执行Maven install操作不能正常编译通过的情况:可以尝试采用:Maven clean→Clean项目→Maven install这三个步骤去解决问题。
  除了可以用常规的Junit进行单元测试之外,我们还可以使用Spring提供的Junit测试框架进行单元测试,在me.gacl.test下新建一个MyBatisTestBySpringTestFramework类,代码如下:

1 package me.gacl.test;
2
3 import java.util.Date;
4 import java.util.UUID;
5 import me.gacl.domain.User;
6 import me.gacl.service.UserServiceI;
7 import org.junit.Test;
8 import org.junit.runner.RunWith;
9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.test.context.ContextConfiguration;
11 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12
13 @RunWith(SpringJUnit4ClassRunner.class)
14 //配置了@ContextConfiguration注解并使用该注解的locations属性指明spring和配置文件之后,
15 @ContextConfiguration(locations = {“classpath:spring.xml”, “classpath:spring-mybatis.xml” })
16 public class MyBatisTestBySpringTestFramework {
17
18 //注入userService
19 @Autowired
20 private UserServiceI userService;
21
22 @Test
23 public void testAddUser(){
24 User user = new User();
25 user.setUserId(UUID.randomUUID().toString().replaceAll("-", “”));
26 user.setUserName(“xdp_gacl_白虎神皇”);
27 user.setUserBirthday(new Date());
28 user.setUserSalary(10000D);
29 userService.addUser(user);
30 }
31
32 @Test
33 public void testGetUserById(){
34 String userId = “fb1c5941094e400b975f10d9a9d602a3”;
35 User user = userService.getUserById(userId);
36 System.out.println(user.getUserName());
37 }
38 }

执行这两个测试方法,是可以正常测试通过的,如下所示:
  
  
  到此,我们框架的整合测试工作就算是全部通过了,整合成功。
4.4、在web服务器中进行测试
  1、编辑web.xml文件,添加spring监听器配置项,内容如下:

<?xml version="1.0" encoding="UTF-8"?>



index.jsp

<listener>
    <description>Spring监听器</description>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- ContextLoaderListener初始化Spring上下文时需要使用到的contextConfigLocation参数 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <!-- 配置spring.xml和spring-mybatis.xml这两个配置文件的位置,固定写法 -->
    <param-value>classpath:spring.xml,classpath:spring-mybatis.xml</param-value>
</context-param>

2、在UserMapper接口中添加一个获取所有用户信息的getAllUser()方法,如下所示:

1 package me.gacl.dao;
2
3 import java.util.List;
4
5 import me.gacl.domain.User;
6
7 public interface UserMapper {
8 int deleteByPrimaryKey(String userId);
9
10 int insert(User record);
11
12 int insertSelective(User record);
13
14 User selectByPrimaryKey(String userId);
15
16 int updateByPrimaryKeySelective(User record);
17
18 int updateByPrimaryKey(User record);
19
20 /**获取所有用户信息
21 * @return List
22 */
23 List getAllUser();
24 }

3、在UserMapper.xml文件中编写getAllUser()方法要执行的SQL语句,如下所示:

<?xml version="1.0" encoding="UTF-8" ?> user_id, user_name, user_birthday, user_salary select from t_user where user_id = #{userId,jdbcType=CHAR} delete from t_user where user_id = #{userId,jdbcType=CHAR} insert into t_user (user_id, user_name, user_birthday, user_salary) values (#{userId,jdbcType=CHAR}, #{userName,jdbcType=VARCHAR}, #{userBirthday,jdbcType=DATE}, #{userSalary,jdbcType=DOUBLE}) insert into t_user user_id, user_name, user_birthday, user_salary, #{userId,jdbcType=CHAR}, #{userName,jdbcType=VARCHAR}, #{userBirthday,jdbcType=DATE}, #{userSalary,jdbcType=DOUBLE}, update t_user user_name = #{userName,jdbcType=VARCHAR}, user_birthday = #{userBirthday,jdbcType=DATE}, user_salary = #{userSalary,jdbcType=DOUBLE}, where user_id = #{userId,jdbcType=CHAR} update t_user set user_name = #{userName,jdbcType=VARCHAR}, user_birthday = #{userBirthday,jdbcType=DATE}, user_salary = #{userSalary,jdbcType=DOUBLE} where user_id = #{userId,jdbcType=CHAR}
  <!-- select标签的id属性与UserMapper接口中定义的getAllUser方法要一模一样 -->
<select id="getAllUser" resultMap="BaseResultMap">
    select user_id, user_name, user_birthday, user_salary from t_user
</select>

4、在UserServiceI接口中也添加一个getAllUser()方法,如下:

1 package me.gacl.service;
2
3 import java.util.List;
4
5 import me.gacl.domain.User;
6
7 public interface UserServiceI {
8
9 /**
10 * 添加用户
11 * @param user
12 /
13 void addUser(User user);
14
15 /
*
16 * 根据用户id获取用户
17 * @param userId
18 * @return
19 */
20 User getUserById(String userId);
21
22 /**获取所有用户信息
23 * @return List
24 */
25 List getAllUser();
26 }

5、在UserServiceImpl类中实现getAllUser方法,如下:

1 package me.gacl.service.impl;
2
3 import java.util.List;
4
5 import org.springframework.beans.factory.annotation.Autowired;
6 import org.springframework.stereotype.Service;
7 import me.gacl.dao.UserMapper;
8 import me.gacl.domain.User;
9 import me.gacl.service.UserServiceI;
10
11 /**
12 * @author gacl
13 * 使用@Service注解将UserServiceImpl类标注为一个service
14 * service的id是userService
15 /
16 @Service(“userService”)
17 public class UserServiceImpl implements UserServiceI {
18
19 /
*
20 * 使用@Autowired注解标注userMapper变量,
21 * 当需要使用UserMapper时,Spring就会自动注入UserMapper
22 */
23 @Autowired
24 private UserMapper userMapper;//注入dao
25
26 @Override
27 public void addUser(User user) {
28 userMapper.insert(user);
29 }
30
31 @Override
32 public User getUserById(String userId) {
33 return userMapper.selectByPrimaryKey(userId);
34 }
35
36 @Override
37 public List getAllUser() {
38 return userMapper.getAllUser();
39 }
40 }

6、在src/main/java目录下创建一个me.gacl.web.controller包,然后在me.gacl.web.controller下创建一个UserServlet,如下:

1 package me.gacl.web.controller;
2
3 import java.io.IOException;
4 import java.util.List;
5
6 import javax.servlet.ServletException;
7 import javax.servlet.annotation.WebServlet;
8 import javax.servlet.http.HttpServlet;
9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse;
11
12 import org.springframework.context.ApplicationContext;
13 import org.springframework.web.context.support.WebApplicationContextUtils;
14
15 import me.gacl.domain.User;
16 import me.gacl.service.UserServiceI;
17
18 /**
19 * @author gacl
20 * @WebServlet是Servlet3.0提供的注解,目的是将一个继承了HttpServlet类的普通java类标注为一个Servlet
21 * UserServlet使用了@WebServlet标注之后,就不需要在web.xml中配置了
22 */
23 @WebServlet("/UserServlet")
24 public class UserServlet extends HttpServlet {
25
26 //处理业务逻辑的userService
27 private UserServiceI userService;
28
29 public void doGet(HttpServletRequest request, HttpServletResponse response)
30 throws ServletException, IOException {
31 //获取所有的用户信息
32 List lstUsers = userService.getAllUser();
33 request.setAttribute(“lstUsers”, lstUsers);
34 request.getRequestDispatcher("/index.jsp").forward(request, response);
35 }
36
37 public void doPost(HttpServletRequest request, HttpServletResponse response)
38 throws ServletException, IOException {
39 this.doGet(request, response);
40 }
41
42 public void init() throws ServletException {
43 //在Servlet初始化时获取Spring上下文对象(ApplicationContext)
44 ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
45 //从ApplicationContext中获取userService
46 userService = (UserServiceI) ac.getBean(“userService”);
47 }
48 }

7、编辑index.jsp页面,用于展示查询到的用户信息,内容如下:

<%@ page language=“java” pageEncoding=“UTF-8”%>
<%–引入JSTL核心标签库 --%>
<%@ taglib prefix=“c” uri=“http://java.sun.com/jsp/jstl/core”%>

显示用户信息
<%--遍历lstUsers集合中的User对象 --%>
用户ID用户名用户生日工资
${user.userId}${user.userName}${user.userBirthday}${user.userSalary}

8、执行maven install命令编译项目,然后将项目部署到tomcat服务器中运行,注意,由于要使用Servlet3.0,所以必须将项目部署到tomcat7.x以上的服务器中去运行,如下所示:
  
  输入地址:http://localhost:8080/spring4-mybatis3/UserServlet 访问UserServlet,访问结果如下:
  
  可以看到,t_user表中的用户信息全部查询出来显示到页面上了。这样在web服务器中的测试也正常通过了。
  以上就是Spring4.x与MyBatis3.x整合的全部内容了。编写这个整合例子花了不少时间,使用Maven编译时总是出现莫名其妙的问题,有时候成功,有时候失败,反正很莫名其妙。如果遇到执行Maven install操作不能正常编译通过的情况:可以尝试采用:Maven clean→Clean项目→Maven install这三个步骤去解决问题
mybatis 动态sql语句
mybatis 的动态sql语句是基于OGNL表达式的。可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类:

  1. if 语句 (简单的条件判断)

  2. choose (when,otherwize) ,相当于java 语言中的 switch ,与 jstl 中的choose 很类似.

  3. trim (对包含的内容加上 prefix,或者 suffix 等,前缀,后缀)

  4. where (主要是用来简化sql语句中where条件判断的,能智能的处理 and or ,不必担心多余导致语法错误)

  5. set (主要用于更新时)

  6. foreach (在实现 mybatis in 语句查询时特别有用)
    下面分别介绍这几种处理方式

  7. mybaits if 语句处理 
    CREATE TABLE t_blog (
    id INT (11),
    title VARCHAR (765),
    content VARCHAR (765),
    owner VARCHAR (150)
    );
    INSERT INTO t_blog (id, title, content, owner) VALUES(‘1’,‘java基础’,‘详细描述’,‘唐国强11’);
    INSERT INTO t_blog (id, title, content, owner) VALUES(‘2’,‘中文’,‘中国’,‘ofo11’);
    INSERT INTO t_blog (id, title, content, owner) VALUES(‘3’,‘html’,‘html标签’,‘样式’);
    INSERT INTO t_blog (id, title, content, owner) VALUES(‘6’,‘html’,‘www’,‘eee’);
    INSERT INTO t_blog (id, title, content, owner) VALUES(‘9’,‘中文’,‘111’,‘1111’);
     程序代码


        select * from t_blog where 1 = 1
        
            and title = #{title}
        
        
            and content = #{content}
        
        
            and owner = #{owner}
        
    
工具类:
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static{
String resource = “mybatis.xml”;
InputStream is = MybatisUtil.class.getClassLoader().getResourceAsStream(resource);

	sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
}

public static SqlSession getSession(boolean tag){
	//tag为真,则自动提交,为假则需要手动提交
	return sqlSessionFactory.openSession(tag);
}

}
测试类:
@Test
public void testBlog(){
SqlSession session=MybatisUtil.getSession(true);
Blog blog=new Blog();
blog.setTitle(“html”);
List list=session.selectList(“com.dao.BlogMapper.dynamicIfTest”,blog);
System.out.println(list);
}
这条语句的意思非常简单,如果你提供了title参数,那么就要满足title=#{title},同样如果你提供了Content和Owner的时候,它们也需要满足相应的条件,之后就是返回满足这些条件的所有Blog,这是非常有用的一个功能,以往我们使用其他类型框架或者直接使用JDBC的时候, 如果我们要达到同样的选择效果的时候,我们就需要拼SQL语句,这是极其麻烦的,比起来,上述的动态SQL就要简单多了

2.2. choose (when,otherwize) ,相当于java 语言中的 switch ,与 jstl 中的choose 很类似 
 程序代码


        select * from t_blog where 1 = 1 
        
            
                and title = #{title}
            
            
                and content = #{content}
            
            
                and owner = “owner1”
            
        
    

when元素表示当when中的条件满足的时候就输出其中的内容,跟JAVA中的switch效果差不多的是按照条件的顺序,当when中有条件满足的时候,就会跳出choose,即所有的when和otherwise条件中,只有一个会输出,当所有的我很条件都不满足的时候就输出otherwise中的内容。所以上述语句的意思非常简单, 当title!=null的时候就输出and titlte = #{title},不再往下判断条件,当title为空且content!=null的时候就输出and content = #{content},当所有条件都不满足的时候就输出otherwise中的内容。

3.trim (对包含的内容加上 prefix,或者 suffix 等,前缀,后缀) 
 程序代码


        select * from t_blog 
        
            
                title = #{title}
            
            
                and content = #{content}
            
            
                or owner = #{owner}
            
        
    

trim元素的主要功能是可以在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀,与之对应的属性是prefix和suffix;可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,对应的属性是prefixOverrides和suffixOverrides;正因为trim有这样的功能,所以我们也可以非常简单的利用trim来代替where元素的功能

  1. where (主要是用来简化sql语句中where条件判断的,能智能的处理 and or 条件
     程序代码


        select * from t_blog 
        
            
                title = #{title}
            
            
                and content = #{content}
            
            
                and owner = #{owner}
            
        
    

where元素的作用是会在写入where元素的地方输出一个where,另外一个好处是你不需要考虑where元素里面的条件输出是什么样子的,MyBatis会智能的帮你处理,如果所有的条件都不满足那么MyBatis就会查出所有的记录,如果输出后是and 开头的,MyBatis会把第一个and忽略,当然如果是or开头的,MyBatis也会把它忽略;此外,在where元素中你不需要考虑空格的问题,MyBatis会智能的帮你加上。像上述例子中,如果title=null, 而content != null,那么输出的整个语句会是select * from t_blog where content = #{content},而不是select * from t_blog where and content = #{content},因为MyBatis会智能的把首个and 或 or 给忽略。
3和4都可以自动判断要不要加and和or
5.set (主要用于更新时) 
 程序代码


        update t_blog
        
            
                title = #{title},
            
            
                content = #{content},
            
            
                owner = #{owner}
            
        
        where id = #{id}
    

set元素主要是用在更新操作的时候,它的主要功能和where元素其实是差不多的,主要是在包含的语句前输出一个set,然后如果包含的语句是以逗号结束的话将会把该逗号忽略,如果set包含的内容为空的话则会出错。有了set元素我们就可以动态的更新那些修改了的字段

  1. foreach (在实现 mybatis in 语句查询时特别有用) 
    foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach元素的属性主要有item,index,collection,open,separator,close。item表示集合中每一个元素进行迭代时的别名,index指定一个名字,用于表示在迭代过程中,每次迭代到的位置,open表示该语句以什么开始,separator表示在每次进行迭代之间以什么符号作为分隔符,close表示以什么结束,在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:
    如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
    如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
    如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Map的,map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key

1.1.单参数List的类型
 程序代码


        select * from t_blog where id in
        
            #{item}
        
    

上述collection的值为list,对应的Mapper是这样的
 程序代码

public List dynamicForeachTest(List ids);

测试代码
 程序代码

@Test
    public void dynamicForeachTest() {
        SqlSession session = Util.getSqlSessionFactory().openSession();
        BlogMapper blogMapper = session.getMapper(BlogMapper.class);
        List ids = new ArrayList();
        ids.add(1);
        ids.add(3);
        ids.add(6);
        List blogs = blogMapper.dynamicForeachTest(ids);
        for (Blog blog : blogs)
            System.out.println(blog);
        session.close();
    }

2.数组类型的参数
 程序代码


        select * from t_blog where id in
        
            #{item}
        
    

对应mapper
 程序代码

public List dynamicForeach2Test(int[] ids);

  1. Map 类型的参数
     程序代码


        select * from t_blog where title like “%”#{title}"%" and id in
        
            #{item}
        
    

mapper 应该是这样的接口:
 程序代码

public List dynamicForeach3Test(Map<String, Object> params);

通过以上方法,就能完成一般的mybatis 的 动态SQL 语句.最常用的就是  if where foreach这几个,一定要重点掌握.
foreach属性
属性 描述
item 循环体中的具体对象。支持属性的点路径访问,如item.age,item.info.details。
具体说明:若collection属性为list或array,则item代表list或array里面的一个元素。若collection属性对应一个map,则item代表的是map中的value集合中的单个value
该参数为必选。
collection foreach遍历的对象,作为入参时,List对象默认用list代替作为键,数组对象有array代替作为键,Map对象没有默认的键。也就是传入的集合(list,array,map)的名字,这个名字可以在foreach里面随便引用)
当然在作为入参时可以使用@Param(“params”)来设置键,设置keyName后,list,array将会失效。 除了入参这种情况外,还有一种作为参数对象的某个字段的时候。举个例子:
如果User有属性List ids。入参是User对象,那么这个collection = “ids”
如果User有属性Ids ids;其中Ids是个对象,Ids有个属性List id;入参是User对象,那么collection = “ids.id
如果传入参数类型为map,这个入参有注解@Param(“params”),则map的所有的key集合可以写成params.keys,所有值集合可以写成params.values。这样foreach就可以对key集合或值集合进行迭代了。
上面只是举例,具体collection等于什么,就看你想对那个元素做循环。
该参数为必选。
separator 元素之间的分隔符,例如在in()的时候,separator=",“会自动在元素中间用“,“隔开,避免手动输入逗号导致sql错误,如in(1,2,)这样。该参数可选。
open foreach代码的开始符号,一般是(和close=”)“合用。常用在in(),values()时。该参数可选。
close foreach代码的关闭符号,一般是)和open=”("合用。常用在in(),values()时。该参数可选。
index 在list和数组中,index是元素的序号,在map中,index是元素的key,该参数可选。
 
 
实现
          有了以上基础就可以实现我们想要的功能:
首先,在mapper对应的dao中使用@param注解,显式指定集合参数类的别名(列表和数组有默认的别名list和array):
1.public interface CrawDao {  
2.  
3.    public void saveNewNews(@Param(“params”)Map<String, String> params);  
4.      
5.}  
 
第二步,在mapper的xml文件里对map的key进行迭代:
1.<?xml version="1.0" encoding="UTF-8"?>  
2.  
3.  
4.  
5.  
6.     
7.         insert ignore into tb_news   
8.           
9.            KaTeX parse error: Expected 'EOF', got '#' at position 165: …13.            #̲{params[{key}]}  
14.           
15.     
16.  
 
通过以上两步就动态的获取了列名,并对对应的列赋值。

基于MyBatis3.0.6的基本操作介绍
每 一 个 MyBatis 的 应 用 程 序 都 以 一 个 SqlSessionFactory 对 象 的 实 例 为 核 心 。SqlSessionFactory本身是由SqlSessionFactoryBuilder创建的,一般而言,在一个应用中,一个数据库只会对应一个SqlSessionFactory,所以一般我们都把SqlSessionFactory定义成单例模式,或通过Spring等进行注入。
SqlSessionFactoryBuilder创建SqlSessionFactory的方法有:
 
Java代码  
1.SqlSessionFactory build(InputStream inputStream)  
2.SqlSessionFactory build(InputStream inputStream, String environment)  
3.SqlSessionFactory build(InputStream inputStream, Properties properties)  
4.SqlSessionFactory build(InputStream inputStream, String env, Properties props)  
5.SqlSessionFactory build(Configuration config)  
 
这些方法主要设计到的参数有InputStream,environment,properties,其中InputStream是从配置文件中获取的一个输入流;environment表示在配置文件里面配置的众多的environment中,当前要使用的是哪一个environment,包括数据源和事务,缺省则使用默认的environment;使用properties,MyBatis则会加载对应的属性或文件,它们可以在配置文件中使用。 
 
 
从XML中构建SqlSessionFactory
 
Java代码  
1.private static SqlSessionFactory sqlSessionFactory = null;  
2.      
3.    static {  
4.        try {  
5.            InputStream is = Resources.getResourceAsStream(“config/mybatis_config.xml”);  
6.            sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);  
7.        } catch (IOException e) {  
8.            // TODO Auto-generated catch block  
9.            e.printStackTrace();  
10.        }  
11.          
12.    }  
13.      
14.    public static SqlSessionFactory getSqlSessionFactory() {  
15.        return sqlSessionFactory;  
16.    }  
 
下面讲讲配置文件的基本结构:
mybatis的配置文件一般包括如下几个部分:
properties:properties用于定义或导入属性,然后在后面的环境中使用
settings:settings用于设置一些mybatis在运行时的行为方式,具体的设置信息可以查看mybatis的文档
typeAliases:typeAliases是为系统中的Java类型指定一个较短的别名
environments:MyBatis 可以配置多种环境。这会帮助你将 SQL 映射应用于多种数据库之中。
 
Xml代码  
1.  
2.          
3.              
4.              
5.                  
6.                  
7.                  
8.                  
9.              
10.          
11.      
由于MyBatis可以配置多个environment,所以可以在创建SqlSessionFactory的时候指定具体的环境来创建特定的环境下的SqlSessionFactory,  不指定则使用默认的环境。
 
 
transactionManager
在 MyBatis 中有两种事务管理器类型(也就是 type=”[JDBC|MANAGED]”):
JDBC – 这个配置直接简单使用了 JDBC 的提交和回滚设置。 它依赖于从数据源得 到的连接来管理事务范围。
MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接。而它会让 容器来管理事务的整个生命周期(比如 Spring 或 JEE 应用服务器的上下文) 默认 情况下它会关闭连接。 然而一些容器并不希望这样, 因此如果你需要从连接中停止 它,将 closeConnection 属性设置为 false。
dataSource
dataSource 元素使用基本的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
许多 MyBatis 的应用程序将会按示例中的例子来配置数据源。 然而它并不是必须的。 要知道为了方便使用延迟加载,数据源才是必须的。
有三种内建的数据源类型(也就是 type=”???”):
UNPOOLED – 这个数据源的实现是每次被请求时简单打开和关闭连接。它有一点慢, 这是对简单应用程序的一个很好的选择, 因为它不需要及时的可用连接。 不同的数据库对这 个的表现也是不一样的, 所以对某些数据库来说配置数据源并不重要, 这个配置也是闲置的。 UNPOOLED 类型的数据源仅仅用来配置以下 5 种属性:
driver – 这是 JDBC 驱动的 Java 类的完全限定名(如果你的驱动包含,它也不是 数据源类)。
url – 这是数据库的 JDBC URL 地址。
username – 登录数据库的用户名。
password – 登录数据库的密码。
defaultTransactionIsolationLevel – 默认的连接事务隔离级别。
作为可选项,你可以传递数据库驱动的属性。要这样做,属性的前缀是以“driver.”开 头的,例如:
driver.encoding=UTF8
这 样 就 会 传 递 以 值 “ UTF8 ” 来 传 递 属 性 “ encoding ”, 它 是 通 过 DriverManager.getConnection(url,driverProperties)方法传递给数据库驱动。
POOLED – 这是 JDBC 连接对象的数据源连接池的实现,用来避免创建新的连接实例 时必要的初始连接和认证时间。这是一种当前 Web 应用程序用来快速响应请求很流行的方 法。
除了上述(UNPOOLED)的属性之外,还有很多属性可以用来配置 POOLED 数据源:
poolMaximumActiveConnections – 在任意时间存在的活动(也就是正在使用)连 接的数量。默认值:10
poolMaximumIdleConnections – 任意时间存在的空闲连接数。
poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检查的时间。默认 值:20000 毫秒(也就是 20 秒)
poolTimeToWait – 这是给连接池一个打印日志状态机会的低层次设置,还有重新 尝试获得连接, 这些情况下往往需要很长时间 为了避免连接池没有配置时静默失 败)。默认值:20000 毫秒(也就是 20 秒)
poolPingQuery – 发送到数据的侦测查询,用来验证连接是否正常工作,并且准备 接受请求。默认是“NO PING QUERY SET” ,这会引起许多数据库驱动连接由一 个错误信息而导致失败。
poolPingEnabled – 这是开启或禁用侦测查询。如果开启,你必须用一个合法的 SQL 语句(最好是很快速的)设置 poolPingQuery 属性。默认值:false。
poolPingConnectionsNotUsedFor – 这是用来配置 poolPingQuery 多次时间被用一次。 这可以被设置匹配标准的数据库连接超时时间, 来避免不必要的侦测。 默认值: 0(也就是所有连接每一时刻都被侦测-但仅仅当 poolPingEnabled 为 true 时适用)。
JNDI – 这个数据源的实现是为了使用如 Spring 或应用服务器这类的容器, 容器可以集 中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。这个数据源配置只需要两个属 性:
initial_context – 这 个 属 性 用 来 从 初 始 上 下 文 中 寻 找 环 境 ( 也 就 是 initialContext.lookup(initial——context) 。这是个可选属性,如果被忽略,那么 data_source 属性将会直接以 initialContext 为背景再次寻找。
data_source – 这是引用数据源实例位置的上下文的路径。它会以由 initial_context 查询返回的环境为背景来查找,如果 initial_context 没有返回结果时,直接以初始 上下文为环境来查找。
再之后就是Mapper了,Mapper就是映射SQL语句的,首先要告诉mybatis要到哪里去找这些SQL语句,即指定资源位置。
Java代码  
1.  
2.          
3.    

下面是我在测试过程中的一个简单的配置文件:
Xml代码  
1.<?xml version="1.0" encoding="UTF-8" ?>  
2.  
5.  
6.      
7.      
8.          
9.      
10.      
11.          
12.              
13.              
14.                  
15.                  
16.                  
17.                  
18.              
19.          
20.      
21.      
22.          
23.      
24.  
 
在上面配置文件中导入了一个外部的属性文件,MyBatis配置文件中的属性引入可以是直接包含在properties元素中的,也可以是利用properties元素从外部引入的,还可以是在创建SqlSessionFactory的时候,作为一个参数properties传入。既然MyBatis配置文件中的属性可以从这么多地方引入,那就牵涉到一个优先级的问题,MyBatis将会按照下面的顺序来寻找它们:
先是配置文件中,properties元素体中的属性被读取
再是利用properties元素从外部引入的属性文件中的属性被读取,会覆盖前面读取的相同的属性
最后是创建SqlSessionFactory时传入的properties中的属性被读取,同样会覆盖前面相同的属性

在有了SqlSessionFactory之后就是获取特定的SqlSession了,在使用mybatis的过程中每一个操作都是离不开SqlSession的,所以获取SqlSession是相当重要的。此外,SqlSession是不能被共享、线程不安全的,所以在每次需要SqlSession的时候都应该打开一个,然后在用完了之后再把它关上。
Java代码  
1.SqlSession session = sqlSessionFactory.openSession();  
 
SqlSessionFactory中湖区SqlSession的方法有:
Java代码  
1.SqlSession openSession()  
2.SqlSession openSession(boolean autoCommit)  
3.SqlSession openSession(Connection connection)  
4.SqlSession openSession(TransactionIsolationLevel level)  
5.SqlSession openSession(ExecutorType execType,TransactionIsolationLevel level)  
6.SqlSession openSession(ExecutorType execType)  
7.SqlSession openSession(ExecutorType execType, boolean autoCommit)  
8.SqlSession openSession(ExecutorType execType, Connection connection)  
9.Configuration getConfiguration();  
它们的主要区别在于:
Transaction (事务): 你想为 session 使用事务或者使用自动提交
Connection (连接): 你想 MyBatis 获得来自配置的数据源的连接还是提供你自己
Execution (执行): 你想 MyBatis 复用预处理语句和/或批量更新语句(包括插入和 删除)
 默认的opensession方法没有参数,它会创建有如下特性的SqlSession:
会开启一个事务,也就是不自动提交
连接对象会从当前正在使用的environment中的数据源中得到
事务隔离级别将会使用驱动或数据源的默认值
预处理语句不会被复用,也不会批量更新语句
ExecutorType有三个值:
ExecutorType.SIMPLE 它会为每个语句的执行创建一个新的预处理语句
ExecutorType.REUSE 它会复用预处理语句
ExecutorType.BATCH 这个执行器会批量执行更新语句

mybatis的基本操作就是增、删、改、查,即insert、delete、update和select。在进行这些基本的操作的时候可以直接利用SqlSession访问Mapper配置文件里面的映射来进行,也可以利用与Mapper配置文件相对应的Mapper接口来进行操作,条件是Mapper接口中定义的方法的参数和返回值要与Mapper配置文件中定义的参数和返回值相同。此外,在使用Mapper接口的时候,对应的SQL语句是可以写在Mapper配置文件中的,也可以直接利用对应的注解在Mapper接口中对应的方法上进行标明,这将在下面的示例代码中看到。

下面是一系列的示例代码:
先贴一个用于获取SqlSessionFactory的工具类:
Java代码  
1.import java.io.IOException;  
2.import java.io.InputStream;  
3.  
4.import org.apache.ibatis.io.Resources;  
5.import org.apache.ibatis.session.SqlSessionFactory;  
6.import org.apache.ibatis.session.SqlSessionFactoryBuilder;  
7.  
8.public class Util {  
9.      
10.    private static SqlSessionFactory sqlSessionFactory = null;  
11.      
12.    static {  
13.        try {  
14.            InputStream is = Resources.getResourceAsStream(“config/mybatis_config.xml”);  
15.            sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);  
16.        } catch (IOException e) {  
17.            // TODO Auto-generated catch block  
18.            e.printStackTrace();  
19.        }  
20.          
21.    }  
22.      
23.    public static SqlSessionFactory getSqlSessionFactory() {  
24.        return sqlSessionFactory;  
25.    }  
26.      
27.}  
 
mybatis的配置文件:
Xml代码  
1.<?xml version="1.0" encoding="UTF-8" ?>  
2.  
5.  
6.      
7.      
8.          
9.      
10.      
11.          
12.              
13.              
14.                  
15.                  
16.                  
17.                  
18.              
19.          
20.      
21.      
22.          
23.      
24.  
 
BlogMapper.xml
Xml代码  
1.<?xml version="1.0" encoding="UTF-8" ?>  
2.  
5.  
6.  
7.   
8.      
9.        insert into t_blog(title,content,owner) values(#{title},#{content},#{owner})  
10.      
11.   
12.      
13.        select * from t_blog where id = #{id}  
14.      
15.   
16.      
17.        update t_blog set title = #{title},content = #{content},owner = #{owner} where id = #{id}  
18.      
19.   
20.      
21.        select * from t_blog  
22.      
23.   
24.      
25.        select * from t_blog where title like “%”#{title}"%"  
26.      
27.   
28.      
29.        delete from t_blog where id = #{id}  
30.      
31.  
SQL映射语句中一些应该注意的问题:
1. resultType的值应该是返回类型的完全名或别名,当返回的结果是一个集合的时候,resultType应为集合中所包含的类型,而不是集合类型,如上面的Blog
2.resultType和resultMap都是表示指定返回结果的,但两者不能同时使用
3.对于Insert映射语句有一个useGeneratedKeys属性,该属性的默认值为false,当该属性的值为true时,在进行插入操作时,mybatis会取到当前正在插入的记录在数据库中的自动递增的主键值,并把它设置给指定的实体的属性,这就需要设置一个keyProperty属性,用于指定实体中表示主键的属性

Blog.java
Java代码  
1.package com.tiantian.mybatis.model;  
2.  
3.public class Blog {  
4.  
5.    private int id;  
6.  
7.    private String title;  
8.  
9.    private String content;  
10.  
11.    private String owner;  
12.  
13.    public int getId() {  
14.        return id;  
15.    }  
16.  
17.    public void setId(int id) {  
18.        this.id = id;  
19.    }  
20.  
21.    public String getTitle() {  
22.        return title;  
23.    }  
24.  
25.    public void setTitle(String title) {  
26.        this.title = title;  
27.    }  
28.  
29.    public String getContent() {  
30.        return content;  
31.    }  
32.  
33.    public void setContent(String content) {  
34.        this.content = content;  
35.    }  
36.  
37.    public String getOwner() {  
38.        return owner;  
39.    }  
40.  
41.    public void setOwner(String owner) {  
42.        this.owner = owner;  
43.    }  
44.  
45.    @Override  
46.    public String toString() {  
47.        return "id: " + id + ", title: " + title + ", content: " + content  
48.                + ", owner: " + owner;  
49.    }  
50.  
51.}  
 
BlogMapper.java
Java代码  
1.package com.tiantian.mybatis.model;  
2.  
3.import java.util.List;  
4.  
5.import org.apache.ibatis.annotations.Delete;  
6.import org.apache.ibatis.annotations.Insert;  
7.import org.apache.ibatis.annotations.Select;  
8.import org.apache.ibatis.annotations.Update;  
9.  
10./** 
11. * 以下的操作1都是把SQL写在配置文件里面的,而操作2都是直接用注解标明要执行的SQL语句 
12. * 因为该Mapper的全名跟BlogMapper.xml文件里面的namespace是一样的,所以不能在这里面 
13. * 用注解定义一个与BlogMapper.xml文件里面同名的映射 
14. * @author andy 
15. * 
16. /  
17.public interface BlogMapper {  
18.  
19.    public Blog selectBlog(int id);  
20.      
21.    @Select(“select * from t_blog where id = #{id}”)  
22.    public Blog selectBlog2(int id);  
23.      
24.    public void insertBlog(Blog blog);  
25.      
26.    @Insert(“insert into t_blog(title,content,owner) values(#{title},#{content},#{owner})”)  
27.    public void insertBlog2(Blog blog);  
28.      
29.    public void updateBlog(Blog blog);  
30.      
31.    @Update(“update t_blog set title=#{title},content=#{content},owner=#{owner} where id=#{id}”)  
32.    public void updateBlog2(Blog blog);  
33.      
34.    public void deleteBlog(int id);   
35.      
36.    @Delete(“delete from t_blog where id = #{id}”)  
37.    public void deleteBlog2(int id);  
38.      
39.    public List selectAll();  
40.      
41.    @Select(“select * from t_blog”)  
42.    public List selectAll2();  
43.      
44.    public List fuzzyQuery(String title);  
45.      
46.    @Select(“select * from t_blog where title like “%”#{title}”%"")  
47.    public List fuzzyQuery2(String title);  
48.      
49.}  
 
Test1.java
Java代码  
1.package com.tiantian.mybatis.test;  
2.  
3.import java.util.List;  
4.  
5.import org.apache.ibatis.session.SqlSession;  
6.import org.junit.Test;  
7.  
8.import com.tiantian.mybatis.model.Blog;  
9.import com.tiantian.mybatis.util.Util;  
10.  
11./

12. * 该系列操作是通过把SQL写在配置文件里面, 
13. * 然后利用SqlSession进行操作的 
14. * @author andy 
15. * 
16. /  
17.public class Test1 {  
18.  
19.    /

20.     * 新增记录 
21.     /  
22.    @Test  
23.    public void testInsertBlog() {  
24.        SqlSession session = Util.getSqlSessionFactory().openSession();  
25.        Blog blog = new Blog();  
26.        blog.setTitle(“中国人”);  
27.        blog.setContent(“五千年的风和雨啊藏了多少梦”);  
28.        blog.setOwner(“天天”);  
29.        session.insert(“com.tiantian.mybatis.model.BlogMapper.insertBlog”, blog);  
30.        session.commit();  
31.        session.close();  
32.    }  
33.      
34.    /

35.     * 查询单条记录 
36.     /  
37.    @Test  
38.    public void testSelectOne() {  
39.        SqlSession session = Util.getSqlSessionFactory().openSession();  
40.        Blog blog = (Blog)session.selectOne(“com.tiantian.mybatis.model.BlogMapper.selectBlog”, 8);  
41.        System.out.println(blog);  
42.        session.close();  
43.    }  
44.      
45.    /

46.     * 修改记录 
47.     /  
48.    @Test  
49.    public void testUpdateBlog() {  
50.        SqlSession session = Util.getSqlSessionFactory().openSession();  
51.        Blog blog = new Blog();  
52.        blog.setId(7);//需要修改的Blog的id  
53.        blog.setTitle(“中国人2”);//修改Title  
54.        blog.setContent(“黄色的脸,黑色的眼,不变是笑容”);//修改Content  
55.        blog.setOwner(“天天2”);//修改Owner  
56.        session.update(“com.tiantian.mybatis.model.BlogMapper.updateBlog”, blog);  
57.        session.commit();  
58.        session.close();  
59.    }  
60.      
61.    /

62.     * 查询所有的记录 
63.     /  
64.    @Test  
65.    public void testSelectAll() {  
66.        SqlSession session = Util.getSqlSessionFactory().openSession();  
67.        List blogs = session.selectList(“com.tiantian.mybatis.model.BlogMapper.selectAll”);  
68.        for (Blog blog:blogs)  
69.            System.out.println(blog);  
70.        session.close();  
71.    }  
72.      
73.    /

74.     * 模糊查询 
75.     /  
76.    @Test  
77.    public void testFuzzyQuery() {  
78.        SqlSession session = Util.getSqlSessionFactory().openSession();  
79.        String title = “中国”;  
80.        List blogs = session.selectList(“com.tiantian.mybatis.model.BlogMapper.fuzzyQuery”, title);  
81.        for (Blog blog:blogs)  
82.            System.out.println(blog);  
83.        session.close();  
84.    }  
85.      
86.    /

87.     * 删除记录 
88.     /  
89.    @Test  
90.    public void testDeleteBlog() {  
91.        SqlSession session = Util.getSqlSessionFactory().openSession();  
92.        session.delete(“com.tiantian.mybatis.model.BlogMapper.deleteBlog”, 8);  
93.        session.commit();  
94.        session.close();  
95.    }  
96.      
97.}  
 
Test2.java
Java代码  
1.package com.tiantian.mybatis.test;  
2.  
3.import java.util.List;  
4.  
5.import org.apache.ibatis.session.SqlSession;  
6.import org.junit.Test;  
7.  
8.import com.tiantian.mybatis.model.Blog;  
9.import com.tiantian.mybatis.model.BlogMapper;  
10.import com.tiantian.mybatis.util.Util;  
11.  
12./

13. * 该系列操作是将SQL语句写在配置文件里面, 
14. * 然后通过对应Mapper接口来进行操作的 
15. * @author andy 
16. * 
17. /  
18.public class Test2 {  
19.  
20.    /

21.     * 新增记录 
22.     /  
23.    @Test  
24.    public void testInsertBlog() {  
25.        SqlSession session = Util.getSqlSessionFactory().openSession();  
26.        Blog blog = new Blog();  
27.        blog.setTitle(“中国人”);  
28.        blog.setContent(“五千年的风和雨啊藏了多少梦”);  
29.        blog.setOwner(“天天”);  
30.        BlogMapper blogMapper = session.getMapper(BlogMapper.class);  
31.        blogMapper.insertBlog(blog);  
32.        session.commit();  
33.        session.close();  
34.    }  
35.  
36.    /

37.     * 查询单条记录 
38.     /  
39.    @Test  
40.    public void testSelectOne() {  
41.        SqlSession session = Util.getSqlSessionFactory().openSession();  
42.        BlogMapper blogMapper = session.getMapper(BlogMapper.class);  
43.        Blog blog = blogMapper.selectBlog(7);  
44.        System.out.println(blog);  
45.        session.close();  
46.    }  
47.  
48.    /

49.     * 修改记录 
50.     /  
51.    @Test  
52.    public void testUpdateBlog() {  
53.        SqlSession session = Util.getSqlSessionFactory().openSession();  
54.        Blog blog = new Blog();  
55.        blog.setId(9);// 需要修改的Blog的id  
56.        blog.setTitle(“中国人2”);// 修改Title  
57.        blog.setContent(“黄色的脸,黑色的眼,不变是笑容”);// 修改Content  
58.        blog.setOwner(“天天2”);// 修改Owner  
59.        BlogMapper blogMapper = session.getMapper(BlogMapper.class);  
60.        blogMapper.updateBlog(blog);  
61.        session.commit();  
62.        session.close();  
63.    }  
64.  
65.    /

66.     * 查询所有记录 
67.     /  
68.    @Test  
69.    public void testSelectAll() {  
70.        SqlSession session = Util.getSqlSessionFactory().openSession();  
71.        BlogMapper blogMapper = session.getMapper(BlogMapper.class);  
72.        List blogs = blogMapper.selectAll();  
73.        for (Blog blog : blogs)  
74.            System.out.println(blog);  
75.        session.close();  
76.    }  
77.  
78.    /

79.     * 模糊查询 
80.     /  
81.    @Test  
82.    public void testFuzzyQuery() {  
83.        SqlSession session = Util.getSqlSessionFactory().openSession();  
84.        BlogMapper blogMapper = session.getMapper(BlogMapper.class);  
85.        String title = “中国”;  
86.        List blogs = blogMapper.fuzzyQuery(title);  
87.        for (Blog blog : blogs)  
88.            System.out.println(blog);  
89.        session.close();  
90.    }  
91.  
92.    /

93.     * 删除记录 
94.     /  
95.    @Test  
96.    public void testDeleteBlog() {  
97.        SqlSession session = Util.getSqlSessionFactory().openSession();  
98.        BlogMapper blogMapper = session.getMapper(BlogMapper.class);  
99.        blogMapper.deleteBlog(10);  
100.        session.commit();  
101.        session.close();  
102.    }  
103.  
104.}  
 
Test3.java
Java代码  
1.package com.tiantian.mybatis.test;  
2.  
3.import java.util.List;  
4.  
5.import org.apache.ibatis.session.SqlSession;  
6.import org.junit.Test;  
7.  
8.import com.tiantian.mybatis.model.Blog;  
9.import com.tiantian.mybatis.model.BlogMapper;  
10.import com.tiantian.mybatis.util.Util;  
11.  
12./

13. * 该系列操作是利用Mapper接口来进行的 
14. * ,然而其相应的SQL语句是通过对应的 
15. * 注解Annotation在Mapper中对应的方法上定义的 
16. * @author andy 
17. * 
18. /  
19.public class Test3 {  
20.  
21.    /

22.     * 新增记录 
23.     /  
24.    @Test  
25.    public void testInsert() {  
26.        SqlSession session = Util.getSqlSessionFactory().openSession();  
27.        BlogMapper blogMapper = session.getMapper(BlogMapper.class);  
28.        Blog blog = new Blog();  
29.        blog.setTitle(“title2”);  
30.        blog.setContent(“content2”);  
31.        blog.setOwner(“owner2”);  
32.        blogMapper.insertBlog2(blog);  
33.        session.commit();  
34.        session.close();  
35.    }  
36.      
37.    /

38.     * 查找单条记录 
39.     /  
40.    @Test  
41.    public void testSelectOne() {  
42.        SqlSession session = Util.getSqlSessionFactory().openSession();  
43.        BlogMapper blogMapper = session.getMapper(BlogMapper.class);  
44.        Blog blog = blogMapper.selectBlog2(1);  
45.        System.out.println(blog);  
46.        session.close();  
47.    }  
48.      
49.    /

50.     * 查找多条记录,返回结果为一集合 
51.     /  
52.    @Test  
53.    public void testSelectAll() {  
54.        SqlSession session = Util.getSqlSessionFactory().openSession();  
55.        BlogMapper blogMapper = session.getMapper(BlogMapper.class);  
56.        List blogs = blogMapper.selectAll2();  
57.        for (Blog blog:blogs)  
58.            System.out.println(blog);  
59.        session.close();  
60.    }  
61.      
62.    /

63.     * 修改某条记录 
64.     /  
65.    @Test  
66.    public void testUpdate() {  
67.        SqlSession session = Util.getSqlSessionFactory().openSession();  
68.        BlogMapper blogMapper = session.getMapper(BlogMapper.class);  
69.        Blog blog = new Blog();  
70.        blog.setId(3);  
71.        blog.setTitle(“title3”);  
72.        blog.setContent(“content3”);  
73.        blog.setOwner(“owner3”);  
74.        blogMapper.updateBlog2(blog);  
75.        session.commit();  
76.        session.close();  
77.    }  
78.      
79.    /

80.     * 删除记录 
81.     */  
82.    @Test  
83.    public void testDelete() {  
84.        SqlSession session = Util.getSqlSessionFactory().openSession();  
85.        BlogMapper blogMapper = session.getMapper(BlogMapper.class);  
86.        blogMapper.deleteBlog2(5);  
87.        session.commit();  
88.        session.close();  
89.    }  
90.      
91.    @Test  
92.    public void testFuzzyQuery() {  
93.        SqlSession session = Util.getSqlSessionFactory().openSession();  
94.        BlogMapper blogMapper = session.getMapper(BlogMapper.class);  
95.        List blogs = blogMapper.fuzzyQuery2(“中国”);  
96.        for (Blog blog:blogs)  
97.            System.out.println(blog);  
98.        session.close();  
99.    }  
100.      
101.}

对应的建表语句:
Java代码  
1.CREATE TABLE t_blog (  
2.  id int(11) NOT NULL AUTO_INCREMENT,  
3.  title varchar(255) DEFAULT NULL,  
4.  content varchar(255) DEFAULT NULL,  
5.  owner varchar(50) DEFAULT NULL,  
6.  PRIMARY KEY (id)  
7.)  
INSERT INTO mybatis.t_blog (title, content, owner) VALUES (‘java基础’, ‘java详解’, ‘牛柯’);
INSERT INTO mybatis.t_blog (title, content, owner) VALUES (‘ofo小黄车’, ‘好骑又漂亮,方便出行’, ‘ofo’);
INSERT INTO mybatis.t_blog (title, content, owner) VALUES (‘html’, ‘html标签’, ‘www’);

MyBatis的动态SQL详解
MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑。
MyBatis中用于实现动态SQL的元素主要有:
 
if
choose(when,otherwise)
trim
where
set
foreach
if就是简单的条件判断,利用if语句我们可以实现某些简单的条件选择。先来看如下一个例子:
Xml代码  
1.  
2.    select * from t_blog where 11 = 1  
3.      
4.        and title = #{title}  
5.      
6.      
7.        and content = #{content}  
8.      
9.      
10.        and owner = #{owner}  
11.      
12.  
这条语句的意思非常简单,如果你提供了title参数,那么就要满足title=#{title},同样如果你提供了Content和Owner的时候,它们也需要满足相应的条件,之后就是返回满足这些条件的所有Blog,这是非常有用的一个功能,以往我们使用其他类型框架或者直接使用JDBC的时候, 如果我们要达到同样的选择效果的时候,我们就需要拼SQL语句,这是极其麻烦的,比起来,上述的动态SQL就要简单多了。
 
choose元素的作用就相当于JAVA中的switch语句,基本上跟JSTL中的choose的作用和用法是一样的,通常都是与when和otherwise搭配的。看如下一个例子:
Xml代码  
1.  
2.    select * from t_blog where 11 = 1   
3.      
4.          
5.            and title = #{title}  
6.          
7.          
8.            and content = #{content}  
9.          
10.          
11.            and owner = “owner1”  
12.          
13.      
14.  
when元素表示当when中的条件满足的时候就输出其中的内容,跟JAVA中的switch效果差不多的是按照条件的顺序,当when中有条件满足的时候,就会跳出choose,即所有的when和otherwise条件中,只有一个会输出,当所有的我很条件都不满足的时候就输出otherwise中的内容。所以上述语句的意思非常简单, 当title!=null的时候就输出and titlte = #{title},不再往下判断条件,当title为空且content!=null的时候就输出and content = #{content},当所有条件都不满足的时候就输出otherwise中的内容。
 
where语句的作用主要是简化SQL语句中where中的条件判断的,先看一个例子,再解释一下where的好处。
Xml代码  
1.  
2.    select * from t_blog   
3.      
4.          
5.            title = #{title}  
6.          
7.          
8.            and content = #{content}  
9.          
10.          
11.            and owner = #{owner}  
12.          
13.      
14.  
 where元素的作用是会在写入where元素的地方输出一个where,另外一个好处是你不需要考虑where元素里面的条件输出是什么样子的,MyBatis会智能的帮你处理,如果所有的条件都不满足那么MyBatis就会查出所有的记录,如果输出后是and 开头的,MyBatis会把第一个and忽略,当然如果是or开头的,MyBatis也会把它忽略;此外,在where元素中你不需要考虑空格的问题,MyBatis会智能的帮你加上。像上述例子中,如果title=null, 而content != null,那么输出的整个语句会是select * from t_blog where content = #{content},而不是select * from t_blog where and content = #{content},因为MyBatis会智能的把首个and 或 or 给忽略。
 
trim元素的主要功能是可以在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀,与之对应的属性是prefix和suffix;可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,对应的属性是prefixOverrides和suffixOverrides;正因为trim有这样的功能,所以我们也可以非常简单的利用trim来代替where元素的功能,示例代码如下:
Xml代码  
1.  
2.    select * from t_blog   
3.      
4.          
5.            title = #{title}  
6.          
7.          
8.            and content = #{content}  
9.          
10.          
11.            or owner = #{owner}  
12.          
13.      
14.  
 
set元素主要是用在更新操作的时候,它的主要功能和where元素其实是差不多的,主要是在包含的语句前输出一个set,然后如果包含的语句是以逗号结束的话将会把该逗号忽略,如果set包含的内容为空的话则会出错。有了set元素我们就可以动态的更新那些修改了的字段。下面是一段示例代码:
Xml代码  
1.  
2.    update t_blog  
3.      
4.          
5.            title = #{title},  
6.          
7.          
8.            content = #{content},  
9.          
10.          
11.            owner = #{owner}  
12.          
13.      
14.    where id = #{id}  
15.  
 上述示例代码中,如果set中一个条件都不满足,即set中包含的内容为空的时候就会报错。
 
foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach元素的属性主要有item,index,collection,open,separator,close。item表示集合中每一个元素进行迭代时的别名,index指定一个名字,用于表示在迭代过程中,每次迭代到的位置,open表示该语句以什么开始,separator表示在每次进行迭代之间以什么符号作为分隔符,close表示以什么结束,在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:
1.如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
2.如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
3.如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Map的,map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key
下面分别来看看上述三种情况的示例代码:
1.单参数List的类型:
Xml代码  
1.  
2.    select * from t_blog where id in  
3.      
4.        #{item}  
5.      
6.  
 上述collection的值为list,对应的Mapper是这样的
Java代码  
1.public List dynamicForeachTest(List ids);  
 测试代码:
Java代码  
1.@Test  
2.public void dynamicForeachTest() {  
3.    SqlSession session = Util.getSqlSessionFactory().openSession();  
4.    BlogMapper blogMapper = session.getMapper(BlogMapper.class);  
5.    List ids = new ArrayList();  
6.    ids.add(1);  
7.    ids.add(3);  
8.    ids.add(6);  
9.    List blogs = blogMapper.dynamicForeachTest(ids);  
10.    for (Blog blog : blogs)  
11.        System.out.println(blog);  
12.    session.close();  
13.}  
 
2.单参数array数组的类型:
Xml代码  
1.  
2.    select * from t_blog where id in  
3.      
4.        #{item}  
5.      
6.  
 上述collection为array,对应的Mapper代码:
Java代码  
1.public List dynamicForeach2Test(int[] ids);  
 对应的测试代码:
Java代码  
1.@Test  
2.public void dynamicForeach2Test() {  
3.    SqlSession session = Util.getSqlSessionFactory().openSession();  
4.    BlogMapper blogMapper = session.getMapper(BlogMapper.class);  
5.    int[] ids = new int[] {1,3,6,9};  
6.    List blogs = blogMapper.dynamicForeach2Test(ids);  
7.    for (Blog blog : blogs)  
8.        System.out.println(blog);  
9.    session.close();  
10.}  
 
3.自己把参数封装成Map的类型
Xml代码  
1.  
2.    select * from t_blog where title like “%”#{title}"%" and id in  
3.      
4.        #{item}  
5.      
6.  
上述collection的值为ids,是传入的参数Map的key,对应的Mapper代码:
Java代码  
1.public List dynamicForeach3Test(Map<String, Object> params);  
  对应测试代码:
Java代码  
1.@Test  
2.public void dynamicForeach3Test() {  
3.    SqlSession session = Util.getSqlSessionFactory().openSession();  
4.    BlogMapper blogMapper = session.getMapper(BlogMapper.class);  
5.    final List ids = new ArrayList();  
6.    ids.add(1);  
7.    ids.add(2);  
8.    ids.add(3);  
9.    ids.add(6);  
10.    ids.add(7);  
11.    ids.add(9);  
12.    Map<String, Object> params = new HashMap<String, Object>();  
13.    params.put(“ids”, ids);  
14.    params.put(“title”, “中国”);  
15.    List blogs = blogMapper.dynamicForeach3Test(params);  
16.    for (Blog blog : blogs)  
17.        System.out.println(blog);  
18.    session.close();  
19.}  
 
 
基础部分可以查看我的另一篇博客:http://haohaoxuexi.iteye.com/blog/1333271
基于3.3.1版本验证的新特性
 本文是基于Mybatis 3.0.6版本所写,笔者最近在查看新版Mybatis(3.3.1)时发现其对动态SQL的支持较以前的3.0.6版本的区别不大,基本一致,但笔者还是发现有以下新特性:
1、foreach标签遍历的集合元素类型是Map.Entry类型时,index属性指定的变量代表对应的Map.Entry的key,item属性指定的变量代表对应的Map.Entry的value。此时如果对应的集合是Map.entrySet,则对应的collection属性用collection。foreach在进行遍历的时候如果传入的参数是List类型,则其collection属性的值可以是list或collection,但如果传入的参数是Set类型,则collection属性的值只能用collection。
Xml代码  
1.  
2.    select * from t_blog where id in  
3.       
4.      
5.        #{key}, #{value}  
6.      
7.  
 其对应的接口方法大概是如下这样:
Java代码  
1.public List dynamicForeachTest(Set<Map.Entry<Integer, Integer>> ids);  
 
2、bind标签,动态SQL中已经包含了这样一个新的标签,它的功能是在当前OGNL上下文中创建一个变量并绑定一个值。有了它以后我们以前的模糊查询就可以改成这个样子:
Xml代码  
1.      
2.       
3.  
4.      select * from t_blog where title like #{titleLike}    
5.  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值