Fork me on GitHub

MyBatis 入门(一)

1. MyBatis 概述

  1. MyBatis 是一个半自动化的持久层框架;
    • 核心SQL,开发人员可以进行优化;
    • SQL和Java编码分开,功能边界清晰,一个专注业务,一个专注数据;
  2. JDBC:
    • SQL 语句夹在Java代码块里,耦合度高,导致硬编码内伤;
    • 维护不易且实际开发需求中SQL是有变化,频繁修改的情况多见;
  3. Hibernate 和 JPA
    • 长难复杂SQL,对于Hibernate而言处理也不容易;
    • 内部自动生产的SQL,不容易做特殊优化;
    • 基于全映射的全自动框架,大量字段的POJO进行部分映射时,比较困难;从而导致数据库性能下降;

2. MyBatis 入门程序搭建

// 创建 Java Project

// 1. 导入 jar 包(3个)
    /*
     * log4j-1.2.17(用来在控制台打印 SQL 语句)
     * mybatis-3.4.1
     * mysql-connector-java (SQL 驱动包)
     */

// 2. 创建 Bean
// cn.itcast.mybatis.bean.Employee.java
public class Employee{
    private Integer id;
    private String lastName;
    private String email;
    // 以数字 0 表示男性, 1 表示女性
    private Integer gender;

    // getter 和 setter 略
}

// 3. 配置文件, 存放在 conf 文件夹下
// 创建 Source Folder, 名称为 conf

// log4j.xml
    (具体见"参考资料"中链接)

// mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver" />
				<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
				<property name="username" value="root" />
				<property name="password" value="root" />
			</dataSource>
		</environment>
	</environments>

	<!-- 将写好的SQL映射文件,一定要注册到全局配置文件中 -->
	<mappers>
		<mapper resource="EmployeeMapper.xml" />
	</mappers>
</configuration>


// EmployeeMapper.xml
// sql 映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.itcast.mybatis.EmployeeMapper">

  <!-- namespace: 命名空间, id: 表示唯一标识, resultType: 返回值类型 -->
  <select id="selectEmp" resultType="cn.itcast.mybatis.bean.Employee">

    <!-- 数据库中 last_name, Bean 类中 lastName, 以下相当于用别名查询 -->
    <!-- #{id} 表示从传递过来的参数中,获取id值 -->
    select id,last_name lastName,email,gender from tbl_employee where id = #{id}
  </select>
</mapper>


// 4. 编写测试类
public class MyBatisTest{

    // 1. 根据mybatis的配置文件(全局配置文件),创建一个 SqlSessionFactory 对象;

    @Test
    public void test() throws IOException{
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resorces.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = 
                                        new SqlSessionFactoryBuilder().build(inputStream);

        // 2. 获取 SqlSession 实例, 使用该实例执行数据库的增删改查;
        //    一个sqlSession,就是代表和数据库的一次会话,用完需要关闭;
        //    sqlSession 是非线程安全的,每次使用都应该去获取新的对象;
        SqlSession openSession = sqlSessionFactory.openSession();

        try{
            // selectOne(String statement, Object parameter) 参数说明:
            //   statement: sql的唯一标识, 建议写法: 命名空间+id
            //   parameter: 执行 sql 要用的参数
            Employee employee = 
                    openSession.selectOne("cn.itcast.mybatis.EmployeeMapper.selectEmp",1);
            System.out.println(employee);
        }finally{
            openSession.close();
        }
    }
}


// 升级版本: 接口式编程
// 使用接口,实现增删改查
// 创建 cn.itcast.mybatis.dao.EmployeeMapper.java 接口
public interface EmployeeMapper{

    // 按id查询
    public Employee getEmpById(Integer id);
}

// 修改 EmployeeMapper.xml
// sql 映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.itcast.mybatis.dao.EmployeeMapper">

  <!--
  namespace: 命名空间,指定为接口的全类名;
  id: 表示唯一标识,接口中的方法名;
  resultType: 返回值类型
  -->

  <select id="getEmpById" resultType="cn.itcast.mybatis.bean.Employee">

    <!-- 数据库中 last_name, Bean 类中 lastName, 以下相当于用别名查询 -->
    <!-- #{id} 表示从传递过来的参数中,获取id值 -->
    select id,last_name lastName,email,gender from tbl_employee where id = #{id}
  </select>
</mapper>

// 修改测试类
public class MyBatisTest{

    // 获取 sqlSessionFactory 对象的方法
    public SqlSessionFactory getSqlSessionFactory() throws IOException{
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        return new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public test02() throws IOException{
        // 1. 获取 sqlSessionFactory 对象
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();

        // 2. 获取 SqlSession 实例
        SqlSession openSession = sqlSessionFactory.openSession();

        try{
            // 3. 获取接口的实现类对象
            //  mybatis 会为接口自动创建一个代理对象, 代理对象去执行增删改查方法
            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);

            // 调用接口实现类的方法
            Employee employee = mapper.getEmpById(1);
            System.out.println(employee);
        }finally{
            // 4. 关闭 SqlSession
            openSession.close();
        }
    }
}

3. MyBatis 全局配置文件

3.1 常用标签介绍

  • <properties>: 引入外部 properties 配置文件的内容;
    • resource属性: 引入类路径下的资源;
    • url属性: 引入网络路径或者磁盘路径下的资源;
  • <settings>: 包含了很多重要的设置项
  • <typeAliases>: 别名处理器,可以给 java 类型起别名,方便使用;
  • <typeHandlers>: 类型处理器, 处理数据库类型与Java类型的转换;
  • <plugins>
  • <environments>: mybatis 可以配置多种环境, default 的值对应<environment>中的 id 值;
  • <databaseIdProvider>: 设置数据库厂商;
  • <mappers>: 将sql映射文件注册到全局配置文件中;
// mybatis-config.xml 配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <properties resource="dbconfig.properties"></properties>

    <!-- 是否开启驼峰命名:即从数据库列名"a_column"到Java属性名 "aColumn" 的类似映射 -->
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <!-- typeAliase, 别名处理器, 别名不区分大小写 -->
    <typeAliases>
        <!-- 默认别名就是类名小写: employee; 也可以使用 alias 指定新的别名 -->
        <typeAlias type="cn.itcast.mybatis.bean.Employee"/>

        <!-- package: 为某个包下的所有类批量起别名
                 name: 指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名(类名小写))

            批量起别名的情况下, 使用 @Alias 注解为某个类型指定新的别名;
        -->
        <package name="cn.itcast.mybatis.bean"/>
    </typeAliases>

	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>

	<!-- 将写好的SQL映射文件,一定要注册到全局配置文件中
            注册配置文件:
                resource: 引入类路径下的sql映射文件;
                url: 引用网络路径或者磁盘路径下的sql映射文件;
            也可以注册接口:
                class: 注册接口
                    1. 有 sql 映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
                    2. 没有 sql 映射文件,所有的 sql 都是利用注解写在接口上;
            备注:
                比较重要,复杂的 Dao 接口,使用 sql 映射文件;
                简单的Dao接口为了开发方便,可以使用注解;
    -->
	<mappers>
		<mapper resource="EmployeeMapper.xml" />
        <mapper class="cn.itcast.mybatis.bean.EmployeeMapper"/>
	</mappers>
</configuration>


// dbconfig.properties 配置文件
jdbc.driver=com.mysql.jdbc.driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root

4. MyBatis 映射文件

// 映射增删改查语句

// EmployeeMapper.java 接口
public interface EmployeeMapper{

    // 单个参数
    public Employee getEmpById(Integer id);

    public void addEmp(Employee employee);

    public void updateEmp(Employee employee);

    public void deleteEmpById(Integer id);

    // 备注:
    //    MyBatis 允许增删改直接定义以下返回值: Integer, Long, Boolean, void
    //    表示增删改影响的行数, 如果行数大于0, Boolean 值为 true;
    // 例如: public boolean updateEmp(Employee employee);
}

// EmployeeMapper.xml 配置文件
<mapper namespace="cn.itcast.mybatis.dao.EmployeeMapper">

<!-- 查询方法 -->
<select id="getEmpById" resultType="cn.itcast.mybatis.bean.Employee">
    select * from tbl_employee where id="#{id}"
</select>

<!-- 添加方法
        自增主键值的获取: MyBatis 也是利用 statement.getGeneratedKeys(); 不过,需要配置
        useGeneratedKeys="true": 表示使用自增主键获取主键值策略
        keyProperty: 指定对应的主键属性,也就是MyBatis获取到主键值以后,将这个值封装给 JavaBean 的哪个属性
-->
<insert id="addEmp" useGeneratedKeys="true" keyProperty="id">
    insert into tbl_employee(last_name,email,gender)
    values(#{lastName},#{email},#{gender})
</insert>

<!--
    Oracle 不支持自增; Oracle 使用序列来模拟自增;
    每次插入的数据的主键,是从序列中拿到的值,
    Oracle 数据库中获取主键的配置:
-->
    <insert id="addEmp" databaseId="oracle">
        <!--
            keyProperty: 查出的主键值封装给 JavaBean 的哪个属性
            order="BEFORE": 当前sql在插入sql之前运行
            resultType: 查询出数据的返回值类型
        -->
        <selectKey keyProperty="id" order="BEFORE" resultType="Integer">
            <!-- 查询主键的sql语句 -->
            select EMPLOYEES_SEQ.nextval from dual
        </selectKey>

        <!-- 插入数据时的主键,是从序列中拿到的 -->
        insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL)
        values(#{id},#{lastName},#{email})
    </insert>


<!-- 更新方法 -->
<update id="updateEmp">
    update tbl_employee
        set last_name=#{lastName},email=#{email},gender=#{gender}
        where id=#{id}
</update>

<!-- 删除方法 -->
<delete id="deleteEmpById">
    delete from tbl_employee where id=#{id}
</delete>

</mapper>

// 编写测试类
public class MyBatisTest{
    // 1. 获取 SqlSessionFactory 方法(同上)

    // 2. 测试方法
    @Test
    public void test01() throws IOException{
        // 1. 获取 SqlSessionFactory 对象
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();

        // 2. 获取 SqlSession 实例
        //       sqlSessionFactory.openSession();  该实例需要手动提交数据
        //       sqlSessionFactory.openSession(true);   该实例可以自动提交数据
        SqlSession openSession = sqlSessionFactory.openSession();

        try{
            // 3. 获取接口的实现类对象
            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);

            // 添加方法
            Employee employee = new Employee(null,"zhangsan","zhangsan@163.com","1");
            mapper.addEmp(employee);
            // 输出主键值
            System.out.println(employee.getId());

            // 更新方法
            Employee employee = new Employee(1,"lisi","lisi@163.com","1");
            mapper.updateEmp(employee);

            // 删除方法
            mapper.deleteEmpById(2);

            // 4. 手动提交
            openSession.commit();
        }finally{
            // 5. 关闭资源
            openSession.close();
        }
    }
}

4.1 MyBatis 映射文件的参数处理

// 示例: 查询方法中带有多个参数

// EmployeeMapper.java 接口
public interface EmployeeMapper{

    // 多个参数
    public Employee getEmpByIdAndLastName(Integer id, String lastName);
}


// EmployeeMapper.xml 配置文件
<mapper namespace="cn.itcast.mybatis.dao.EmployeeMapper">

<!-- 查询方法(多个参数)
     出现异常:  org.apache.ibatis.binding.BindingException:
               Parameter 'id' not found. Available parameters are [0, 1, param1, param2]
-->
<select id="getEmpByIdAndLastName" resultType="cn.itcast.mybatis.bean.Employee">
    select * from tbl_employee where id=#{id} and last_name=#{lastName}
</select>

<!-- 查询方法(多个参数)
        多个参数: mybatis 会做特殊处理
        多个参数会被封装成一个 map,
                其中 key: param1,...paramN, 或者参数的索引
                    value: 传入的参数值
        #{}就是从 map 中获取指定的key值
-->
<select id="getEmpByIdAndLastName" resultType="cn.itcast.mybatis.bean.Employee">
    select * from tbl_employee where id=#{param1} and last_name=#{param2}
</select>

<!-- 查询方法(多个参数)
    也可以使用命名参数的方法: 在封装参数时,明确指定map中的key值:
    接口的写法:
    public Employee getEmpByIdAndLastName(@Param("id")Integer id, 
                                                                            @Param("lastName")String lastName)
-->
<select id="getEmpByIdAndLastName" resultType="cn.itcast.mybatis.bean.Employee">
    select * from tbl_employee where id=#{id} and last_name=#{lastName}
</select>

<!-- 查询方法(多个参数)
    POJO:
        如果多个参数正好是业务逻辑的数据模型,可以直接传入POJO;
        #{属性名}: 取出传入的POJO类的属性值

    Map:
        如果多个参数不是业务模型中的数据,没有对应的POJO,不经常使用, 为了方便,也可以传入Map
        #{key}: 取出map中对应的值
        此时, 接口中的方法:
        public Employee getEmpByMap(Map<String, Object> map);

    TO:
        如果多个参数不是业务模型的数据,但是经常要使用,推荐编写一个 TO(Tranfer Object)数据传输对象

    特别注意: 如果是 Collection(List,Set)类型或者是数组,mybatis 也会特殊处理,把传入的list或
            者数组封装到map中,
            其中,key: Collection(collection), 如果是 List, 还可以使用 key(list),数组(array)
    public Employee getEmpById(List<Integer> ids);
         取出list集合中第一个id的值: #{list[0]}
-->
<select id="getEmpByMap" resultType="cn.itcast.mybatis.bean.Employee">
    select * from tbl_employee where id = #{id} and last_name=#{lastName}
</select>


</mapper>


// 编写测试类
public class MyBatisTest{
    // 1. 获取 SqlSessionFactory 方法(同上)

    // 2. 测试方法
    @Test
    public void test01() throws IOException{
        // 1. 获取 SqlSessionFactory 对象
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();

        // 2. 获取 SqlSession 实例
        //       sqlSessionFactory.openSession();  该实例需要手动提交数据
        //       sqlSessionFactory.openSession(true);   该实例可以自动提交数据
        SqlSession openSession = sqlSessionFactory.openSession();

        try{
            // 3. 获取接口的实现类对象
            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);

            // 4. 查询数据
            Map<String,Object> map = new HashMap<String,Object>();
            map.put("id",1);
            map.put("lastName","zhangsan");
            Employee employee = mapper.getEmpByMap(map);
            System.out.println(employee);

        }finally{
            // 5. 关闭资源
            openSession.close();
        }
    }
}

4.2 参数值的获取

  1. #{}: 可以获取map中的值或者pojo对象属性的值;
  2. ${}: 也可以获取map中的值或者pojo对象属性的值;
  3. 区别:
    • #{}: 是以预编译的形式,将参数设置到sql语句中, PreparedStatement: 可以防止 Sql 注入;
    • ${}: 取出的值直接拼装在sql语句中, 会有安全问题;
    • 大多情况下,应该使用 #{};

4.3 #{}更丰富的用法

  • 参数位置支持的属性: javaType, jdbcType, mode(存储过程), numericScale, resultMap, typeHandler,
    jdbcTypeName;
  • 实际上,通常被设置的是: 可能为空的列名指定 jdbcType;因为mybatis对所有null都映射的是原生Jdbc的 OTHER;
    Oracle 数据库不能正确识别,会报错;
  • #{email,jdbcType=NULL}表示email列为null时,该列的类型为原生Jdbc的 NULL类型;

参考资料

posted @ 2017-11-05 15:15  小a的软件思考  阅读(771)  评论(0编辑  收藏  举报