初识Mybatis

介绍Mybatis之前,我们可以先看一段传统的数据库连接代码:

 1 public class JdbcTest {
 2 
 3     public static void main(String[] args) {
 4         Connection connection = null;
 5         //PreparedStatement是预编译的Statement,通过Statement发起数据库的操作
 6         //PreparedStatement防止sql注入,执行数据库效率高
 7         PreparedStatement preparedStatement = null;
 8         ResultSet resultSet = null;
 9 
10         try {
11             //加载数据库驱动
12             Class.forName("com.mysql.jdbc.Driver");
13 
14             //通过驱动管理类获取数据库链接
15             connection =  DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "1234");
16             //定义sql语句 ?表示占位符
17             String sql = "select * from user where username = ?" ;
18             //获取预处理statement
19             preparedStatement = connection.prepareStatement(sql);
20             //设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
21             preparedStatement.setString(1, "王五");
22             //向数据库发出sql执行查询,查询出结果集
23             resultSet =  preparedStatement.executeQuery();
24             //遍历查询结果集
25             while(resultSet.next()){
26                 System.out.println(resultSet.getString("id")+"  "+resultSet.getString("username"));
27             }
28         } catch (Exception e) {
29             e.printStackTrace();
30         }finally{
31             //释放资源
32             if(resultSet!=null){
33                 try {
34                     resultSet.close();
35                 } catch (SQLException e) {
36                     // TODO Auto-generated catch block
37                     e.printStackTrace();
38                 }
39             }
40             if(preparedStatement!=null){
41                 try {
42                     preparedStatement.close();
43                 } catch (SQLException e) {
44                     // TODO Auto-generated catch block
45                     e.printStackTrace();
46                 }
47             }
48             if(connection!=null){
49                 try {
50                     connection.close();
51                 } catch (SQLException e) {
52                     // TODO Auto-generated catch block
53                     e.printStackTrace();
54                 }
55             }
56         }
57     }
58 }

通过阅读代码可以总结出的几个问题。

1、数据库连接频繁的创建和关闭,缺点浪费数据库的资源,影响操作效率

设想:使用数据库连接池

2、sql语句是硬编码,如果需求变更需要修改sql,就需要修改java代码,需要重新编译,系统不易维护。

设想:将sql语句 统一配置在文件中,修改sql不需要修改java代码。

3、通过preparedStatement向占位符设置参数,存在硬编码( 参数位置,参数)问题。系统不易维护。

设想:将sql中的占位符及对应的参数类型配置在配置文件中,能够自动输入 映射。

4、遍历查询结果集存在硬编码(列名)。

设想:自动进行sql查询结果向java对象的映射(输出映射)。

问题总结之后正式进入Mybatis.....

1.1    mybatis介绍

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis,实质上Mybatis对ibatis进行一些改进。 目前mybatis在github上托管。git(分布式版本控制,当前比较流程)

 

MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。

Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。

1.2    mybatis架构

 初识Mybatis

下面以mapper代理的方式来编写一个简单的增删查改

SqlMapConfig.xml:在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接,解决问题1

 1 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 2         "http://mybatis.org/dtd/mybatis-3-config.dtd">
 3 <configuration>
 4 
 5     <!-- 属性定义 加载一个properties文件 在 properties标签 中配置属性值 -->
 6     <properties resource="jdbc.properties" />
 7     
 8     <!-- 定义别名 -->
 9     <typeAliases>
10         <!-- 单个别名的定义 alias:别名,type:别名映射的类型 -->
11         <!--<typeAlias type="com.emuii.mybatis.pojo.User" alias="user" />-->
12         <!-- 批量别名定义 指定包路径,自动扫描包下边的pojo,定义别名,别名默认为类名(首字母小写或大写) -->
13         <package name="com.emuii.mybatis.pojo" />
14     </typeAliases>
15 
16     <!-- 和spring整合后 environments配置将废除 -->
17     <environments default="development">
18         <environment id="development">
19             <!-- 使用jdbc事务管理 -->
20             <transactionManager type="JDBC" />
21             <!-- 数据库连接池 -->
22             <dataSource type="POOLED">
23                 <property name="driver" value="${jdbc.driver}" />
24                 <property name="url" value="${jdbc.url}" />
25                 <property name="username" value="${jdbc.username}" />
26                 <property name="password" value="${jdbc.password}" />
27             </dataSource>
28         </environment>
29     </environments>
30 
31     <!-- 加载mapper映射 如果将和spring整合后,可以使用整合包中提供的mapper扫描器,此处的mappers不用配置了 -->
32     <mappers>
33         <!-- 通过resource引用mapper的映射文件 -->
34         <mapper resource="sqlmap/User.xml" />
35         <!--<mapper resource="mapper/UserMapper.xml" />-->
36         <!-- 通过class引用mapper接口 class:配置mapper接口全限定名 要求:需要mapper.xml和mapper.java同名并且在一个目录中 -->
37         <!--<mapper class="com.emuii.mybatis.mapper.UserMapper" />-->
38         <!-- 重复加载映射 - Caused by: org.apache.ibatis.binding.BindingException: Type interface com.emuii.mybatis.mapper.UserMapper is already known to the MapperRegistry.-->
39         <!-- 批量mapper配置 -->
40         <package name="com.emuii.mybatis.mapper" />
41     </mappers>
42 
43 </configuration>

UserMapper.xml:将Sql语句配置在XXXXMapper.xml文件中与java代码分离,解决问题2

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE mapper
 3         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5 <!-- namespace命名空间,为了对sql语句进行隔离,方便管理 ,mapper开发dao方式,使用namespace有特殊作用
 6 mapper代理开发时将namespace指定为mapper接口的全限定名
 7  -->
 8 <mapper namespace="com.emuii.mybatis.mapper.UserMapper">
 9     <!-- 在mapper.xml文件中配置很多的sql语句,执行每个sql语句时,封装为MappedStatement对象
10     mapper.xml以statement为单位管理sql语句
11      -->
12 
13     <!-- 根据id查询用户信息 -->
14     <!--
15         id:唯一标识 一个statement
16         #{}:表示 一个占位符,如果#{}中传入简单类型的参数,#{}中的名称随意
17         parameterType:输入 参数的类型,通过#{}接收parameterType输入 的参数
18         resultType:输出结果 类型,不管返回是多条还是单条,指定单条记录映射的pojo类型
19      -->
20     <select id="findUserById" parameterType="int" resultType="user">
21         SELECT * FROM USER WHERE id= #{id}
22     </select>
23 
24     <!-- 根据用户名称查询用户信息,可能返回多条
25     ${}:表示sql的拼接,通过${}接收参数,将参数的内容不加任何修饰拼接在sql中。
26     不能防止sql注入
27      -->
28     <select id="findUserByUsername" parameterType="java.lang.String" resultType="User">
29         select * from user where username like '%${value}%'
30     </select>
31 
32     <!-- 添加新用户
33      parameterType:输入 参数的类型,User对象 包括 username,birthday,sex,address
34     #{}接收pojo数据,可以使用OGNL解析出pojo的属性值
35     #{username}表示从parameterType中获取pojo的属性值
36     selectKey:用于进行主键返回,定义了获取主键值的sql
37     order:设置selectKey中sql执行的顺序,相对于insert语句来说
38     keyProperty:将主键值设置到哪个属性
39     resultType:select LAST_INSERT_ID()的结果 类型
40      -->
41     <insert id="insertUser" parameterType="user">
42         <selectKey keyProperty="id" order="AFTER" resultType="int">
43             SELECT LAST_INSERT_ID()
44         </selectKey>
45         insert into user(username,sex,birthday,address) values(#{username},#{sex},#{birthday},#{address})
46     </insert>
47     <!-- mysql的uuid生成主键 -->
48     <!-- <insert >
49         <selectKey keyProperty="id" order="BEFORE" resultType="string">
50             select uuid()
51         </selectKey>
52 
53         INSERT INTO USER(id,username,birthday,sex,address) VALUES(#{id},#{username},#{birthday},#{sex},#{address})
54     </insert> -->
55 
56     <!-- oracle
57     在执行insert之前执行select 序列.nextval() from dual取出序列最大值,将值设置到user对象 的id属性
58      -->
59     <!-- <insert >
60         <selectKey keyProperty="id" order="BEFORE" resultType="int">
61             select 序列.nextval() from dual
62         </selectKey>
63 
64         INSERT INTO USER(id,username,birthday,sex,address) VALUES(#{id},#{username},#{birthday},#{sex},#{address})
65     </insert> -->
66 
67     <!-- 用户删除  -->
68     <delete id="deleteUserById" parameterType="int">
69         delete from user where id = #{id}
70     </delete>
71 
72     <!-- 用户更新 -->
73     <update id="updateUser" parameterType="User">
74         update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
75     </update>
76 
77 </mapper>

UserMapper.java(和UserMapper.xml对应,两个随意先写哪一个)

 1 package com.emuii.mybatis.mapper;
 2 
 3 import com.emuii.mybatis.pojo.User;
 4 import com.emuii.mybatis.pojo.UserQueryVo;
 5 
 6 import java.util.List;
 7 
11 public interface UserMapper {
12 
13     //根据id查询用户信息
14     public User findUserById(int id) throws Exception;
15     //根据用户名称模糊查询用户列表
16     public List<User> findUserByUsername(String username) throws Exception;
17     //插入用户
18     public void insertUser(User user) throws Exception;
19 
20 }

UserMapperTest.java

 1 package com.emuii.mybatis.mapper;
 2 
 3 import com.emuii.mybatis.pojo.User;
 4 import org.apache.ibatis.io.Resources;
 5 import org.apache.ibatis.session.SqlSession;
 6 import org.apache.ibatis.session.SqlSessionFactory;
 7 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 8 import org.junit.Before;
 9 import org.junit.Test;
10 
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.util.Date;
14 import java.util.List;
15 
16 /**
17  * Create by Leslie on 201814 0004.<br>
18  */
19 public class UserMapperTest {
20 
21     // 会话工厂
22     private SqlSessionFactory sqlSessionFactory;
23 
24     // 创建工厂
25     @Before
26     public void init() throws IOException {
27         // 配置文件
28         String resource = "SqlMapConfig.xml";
29         // 配置文件到输入流
30         InputStream inputStream = Resources.getResourceAsStream(resource);
31         // 创建会话工厂
32         sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
33     }
34 
35     @Test
36     // 根据id查询用户
37     public void testFindUserById() throws Exception {
38         SqlSession sqlSession = sqlSessionFactory.openSession();
39         // 创建代理对象
40         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
41 
42         User user = userMapper.findUserById(1);
43         sqlSession.close();
44 
45         System.out.println(user);
46     }
47 
48     @Test
49     // 根据名称模糊查询用户
50     public void testFindUserByUsername() throws Exception {
51         SqlSession sqlSession = sqlSessionFactory.openSession();
52         // 创建代理对象
53         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
54 
55         List<User> user = userMapper.findUserByUsername("小明");
56         sqlSession.close();
57 
58         System.out.println(user);
59     }
60 
61     @Test
62     // 增加用户
63     public void testInsertUser() throws Exception {
64         SqlSession sqlSession = sqlSessionFactory.openSession();
65         // 创建代理对象
66         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
67         User user = new User();
68         user.setUsername("小米");
69         user.setSex("2");
70         Date date = new Date();
71         user.setBirthday(date);
72         user.setAddress("法师锕");
73 
74         userMapper.insertUser(user);
75         sqlSession.commit();
76         sqlSession.close();
77 
78         System.out.println(user);
79     }
80 
81 }

ok!

问题3解决方式:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型

 1 <!-- 将用户查询条件定义为sql片段
 2      建议对单表的查询条件单独抽取sql片段,提高复用性
 3      注意:不要讲where标签放在sql片段
 4      -->
 5     <sql id="query_user_where">
 6         <!-- 如果userQueryVo中传入查询条件,再进行sql拼接 -->
 7         <!-- test中userCustom.username表示从userQueryVo读取属性值-->
 8         <if test="userCustomer != null">
 9             <if test="userCustomer != null">
10                 and username like '%${userCustomer.username}%'
11             </if>
12             <if test="userCustomer.sex!=null and userCustomer.sex!='' ">
13                 and sex = #{userCustomer.sex}
14             </if>
15             <!-- 等等等等等等。。。 -->
16         </if>
17 
18         <if test="ids!=null">
19             <!-- 根据id集合查询用户信息 -->
20             <!-- 最终拼接的效果:
21             SELECT id ,username ,birthday  FROM USER WHERE username LIKE '%小明%' AND id IN (16,22,25)
22             collection:集合的属性
23             open:开始循环拼接的串
24             close:结束循环拼接的串
25             item:每次循环取到的对象
26             separator:每两次循环中间拼接的串
27              -->
28             <foreach collection="ids" open="AND id IN (" close=")" item="id" separator=",">
29                 #{id}
30             </foreach>
31         </if>
32     </sql>

使用:

 1 <!-- 自定义查询条件查询用户的信息
 2      parameterType:指定包装类型
 3      %${userCustomer.username}% : userCustomer是userQueryVo中的属性,通过OGNL获取属性的值
 4      -->
 5     <select id="findUserList" parameterType="userQueryVo" resultType="user">
 6         SELECT id,username,birthday from user
 7          <!-- 如果userQueryVo中传入查询条件,再进行sql拼接 -->
 8         <where>
 9             <!-- 引用sql片段,如果sql片段和引用处不在同一个mapper必须前边加namespace -->
10             <include refid="query_user_where" />
11             <!-- 下边还有很其它的条件 -->
12             <!-- <include ref>-->
13         </where>
14     </select>
15 
16     <!-- 查询用户记录 -->
17     <select id="findUserCount" parameterType="userQueryVo" resultType="int">
18         SELECT count(*) from user
19         <where>
20             <include refid="query_user_where" />
21         </where>
22     </select>

UserMapper.java

初识Mybatis

UserQueryVo.java

初识Mybatis

Test

 1     @Test
 2     public void testFindUserList() throws Exception {
 3         SqlSession sqlSession = sqlSessionFactory.openSession();
 4         // 创建代理对象
 5         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
 6 
 7         // 构造查询条件
 8         UserQueryVo userQueryVo = new UserQueryVo();
 9 
10         UserCustomer userCustomer = new UserCustomer();
11         userCustomer.setUsername("小明");
12         userCustomer.setSex("1");
13 
14         userQueryVo.setUserCustomer(userCustomer);
15 
16         // id集合
17         List<Integer> ids = new ArrayList<Integer>();
18         ids.add(16);
19         ids.add(22);
20         userQueryVo.setIds(ids);
21 
22         List<User> list = userMapper.findUserList(userQueryVo);
23 //        User list = userMapper.findUserList(userQueryVo);
24 
25         sqlSession.close();
26 
27         System.out.println(list);
28     }
29 
30     @Test
31     public void testFindUserCount() throws Exception {
32         SqlSession sqlSession = sqlSessionFactory.openSession();
33         // 创建代理对象
34         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
35 
36         // 构造查询条件
37         UserQueryVo userQueryVo = new UserQueryVo();
38 
39         UserCustomer userCustomer = new UserCustomer();
40         userCustomer.setUsername("小明");
41 
42         userQueryVo.setUserCustomer(userCustomer);
43 
44         Integer count = userMapper.findUserCount(userQueryVo);
45 //        User list = userMapper.findUserList(userQueryVo);
46 
47         sqlSession.close();
48 
49         System.out.println(count);
50     }

问题四:对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。

解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。

另外:

mybatis与hibernate重要区别

企业开发进行技术选型 ,考虑mybatis与hibernate适用场景。

mybatis:入门简单,程序容易上手开发,节省开发成本 。mybatis需要程序员自己编写sql语句,是一个不完全 的ORM框架,对sql修改和优化非常容易实现 。

mybatis适合开发需求变更频繁的系统,比如:互联网项目。

hibernate:入门门槛高,如果用hibernate写出高性能的程序不容易实现。hibernate不用写sql语句,是一个 ORM框架。

hibernate适合需求固定,对象数据模型稳定,中小型项目,比如:企业OA系统。

总之,企业在技术选型时根据项目实际情况,以降低成本和提高系统 可维护性为出发点进行技术选型。