MyBatis学习笔记(一)

来源:互联网 发布:大数据自动阅卷系统 编辑:程序博客网 时间:2024/03/29 14:01

1 、对原生态jdbc程序中问题总结

jdbc编程步骤:
1、 加载数据库驱动
2、 创建并获取数据库链接
3、 创建jdbc statement对象
4、 设置sql语句
5、 设置sql语句中的参数(使用preparedStatement)
6、 通过statement执行sql并获取结果
7、 对sql执行结果进行解析处理
8、 释放资源(resultSet、preparedstatement、connection)

public static void main(String[] args) {        // 数据库连接        Connection connection = null;        // 预编译的Statement,使用预编译的Statement提高数据库性能        PreparedStatement preparedStatement = null;        // 结果集        ResultSet resultSet = null;        try {            // 加载数据库驱动            Class.forName("com.mysql.jdbc.Driver");            // 通过驱动管理类获取数据库链接            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?charactorEncoding=utf-8",                    "root", "root");            // 定义sql语句?表示占位符            String sql = "select * from user where username=?";            // 获取预编译处理statement            preparedStatement = connection.prepareStatement(sql);            // 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值            preparedStatement.setString(1, "王五");            // 向数据库发出sql执行查询,查询出结果集            resultSet = preparedStatement.executeQuery();            // 遍历查询结果集            while (resultSet.next()) {                System.out.println(resultSet.getString("id") + " " + resultSet.getString("username"));            }        } catch (Exception e) {            e.printStackTrace();        } finally {            // 释放资源            if (resultSet != null) {                try {                    resultSet.close();                } catch (SQLException e) {                    e.printStackTrace();                }            }            if (preparedStatement != null) {                try {                    preparedStatement.close();                } catch (SQLException e) {                    e.printStackTrace();                }            }            if (connection != null) {                try {                    connection.close();                } catch (SQLException e) {                    e.printStackTrace();                }            }        }    }

jdbc问题总结:
1.数据库连接,使用时就创建,不使用立即释放,对数据库进行频繁连接开启和关闭,造成数据库资源浪费,影响 数据库性能。
设想:使用数据库连接池管理数据库连接。
2、将sql语句硬编码到java代码中,如果sql 语句修改,需要重新编译java代码,不利于系统维护。
设想:将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译。
3、向preparedStatement中设置参数,对占位符号位置和设置参数值,硬编码在java代码中,不利于系统维护。
设想:将sql语句及占位符号和参数全部配置在xml中。
4、从resutSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,,不利于系统维护。
设想:将查询的结果集,自动映射成java对象。

2、mybatis框架

MyBatis 本是apache的一个开源项目iBatis
2010年这个项目由apache software foundation 迁移到了google code
再后来托管到github下(https://github.com/mybatis/mybatis-3/releases)

MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身。

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

这里写图片描述

3、入门程序示例

3.1 需求

根据用户id(主键)查询用户信息
根据用户名称模糊查询用户信息
添加用户
删除 用户
更新用户

3.2 环境

mybatis运行环境(jar包):
从https://github.com/mybatis/mybatis-3/releases下载,3.2.7版本
这里写图片描述
lib下:mybatis依赖包
mybatis-3.2.7.jar:核心包
mybatis-3.2.7.pdf,操作指南

另需:mysql的驱动包

3.3 工程结构

这里写图片描述

3.4 log4j.properties配置

mybatis默认使用log4j作为输出日志信息。
在开发环境下日志级别要设置成DEBUG,生产环境设置成info或error

# Global logging configurationlog4j.rootLogger=DEBUG, stdout# Console output...log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

3.5 SqlMapConfig.xml

SqlMapConfig.xml是mybatis核心配置文件,配置mybatis的数据源、事务管理。

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration>    <!-- 和spring整合后 environments配置将废除-->    <environments default="development">        <environment id="development">        <!-- 使用jdbc事务管理-->            <transactionManager type="JDBC" />        <!-- 数据库连接池-->            <dataSource type="POOLED">                <property name="driver" value="com.mysql.jdbc.Driver" />                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />                <property name="username" value="root" />                <property name="password" value="root" />            </dataSource>        </environment>    </environments></configuration>

3.6 根据用户id(主键)查询用户信息

3.6.1 创建PO类

Po类作为mybatis进行sql映射使用,po类通常与数据库表对应,User.java如下:

Public class User {    private int id;    private String username;// 用户姓名    private String sex;// 性别    private Date birthday;// 生日    private String address;// 地址get/set……toString……}

3.6.2 映射文件

映射文件命名:
User.xml(原始ibatis命名)
mapper代理开发映射文件名称叫XXXMapper.xml,比如:UserMapper.xml、ItemsMapper.xml

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- namespace命名空间,用于隔离sql语句,对sql进行分类化管理 --><mapper namespace="test">    <!-- 在映射文件中配置很多sql语句 -->    <!-- 通过select执行数据库查询         id:标识 映射文件中的sql                将SQL语句封装到mapperStatement对象中,所以将id称为statement的id         parameterType:指定输入参数类型,mybatis通过ognl从输入对象中获取参数值拼接在sql中。         #{}表示一个占位符号,可以有效防止sql注入,可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。         ${}表示拼接sql串,通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。         resultType:指定输出结果单条记录的类型,mybatis将sql查询结果的一行记录数据映射为resultType指定类型的对象。     -->     <!-- 通过id查询用户表的记录 -->     <select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.po.User">        SELECT * FROM USER WHERE id=#{id}     </select></mapper>

3.6.3 在SqlMapConfig.xml中加载映射文件

在< configuration>< /configuration>标签下添加

    <!-- 加载映射文件 -->    <mappers>        <mapper resource="sqlmap/User.xml"/>    </mappers>

3.6.4 程序编写

//根据ID查询用户信息,得到一个记录    @Test    public void findUserByIdTest(){        //mybatis配置文件        String resource="SqlMapconfig.xml";        SqlSession sqlSession=null;        try {            //得到配置文件流            InputStream inputStream = Resources.getResourceAsStream(resource);            //创建会话工厂,传入mybatis的配置文件信息            SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);            //通过工厂得到SqlSession            sqlSession=sqlSessionFactory.openSession();            //通过SqlSession操作数据库,selectOne查询一条记录,selectList可以查询一条或多条记录。            //第一个参数:映射文件中statement的id,(namespace.statement的id)            //第二个参数:传入sql语句的参数值(必须为映射文件中parameterType指定的类型)            //返回结果为映射文件中resultType指定类型的对象            User user=sqlSession.selectOne("test.findUserById",1);            System.out.println(user);        } catch (IOException e) {            e.printStackTrace();        }finally{            //释放资源            if(sqlSession!=null){                sqlSession.close();            }        }    }

3.7 根据用户名称模糊查询用户信息

User.xml 映射文件增加

<!-- ${}表示拼接sql串,通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。     可能引起SQL注入,所以不建议使用--> <!-- 根据用户名模糊查询用户信息 -->     <select id="findUserByName" parameterType="java.lang.String" resultType="cn.itcast.mybatis.po.User">        select * from user where username like '%${value}%'     </select>
// 根据用户名称模糊查询用户列表    @Test    public void findUserByNameTest() {        // mybatis配置文件        String resource = "SqlMapconfig.xml";        SqlSession sqlSession = null;        try {            // 得到配置文件流            InputStream inputStream = Resources.getResourceAsStream(resource);            // 创建会话工厂,传入mybatis的配置文件信息            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);            // 通过工厂得到SqlSession            sqlSession = sqlSessionFactory.openSession();            // 通过SqlSession操作数据库,selectOne查询一条记录,selectList可以查询一条或多条记录。            List<User> userlist = sqlSession.selectList("test.findUserByName", "小明");            System.out.println(userlist);        } catch (IOException e) {            e.printStackTrace();        } finally {            // 释放资源            if (sqlSession != null) {                sqlSession.close();            }        }    }

3.8 添加用户

3.8.1 代码

User.xml 映射文件增加

     <!-- 添加用户     parameterType:指定输入参数类型是pojo(Plain Ordinary Java Object)简单的Java对象     #{}中指定pojo的属性名,接收到pojo对象的属性值,mybatis通过ognl获取对象的属性值     user.id 为自增字段,不用写      -->      <insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">        insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})      </insert>
    //添加用户信息    @Test    public void insertUserTest() {        // mybatis配置文件        String resource = "SqlMapconfig.xml";        SqlSession sqlSession = null;        try {            // 得到配置文件流            InputStream inputStream = Resources.getResourceAsStream(resource);            // 创建会话工厂,传入mybatis的配置文件信息            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);            // 通过工厂得到SqlSession            sqlSession = sqlSessionFactory.openSession();            //插入用户对象            User user=new User();            user.setUsername("王小军");            user.setBirthday(new Date());            user.setSex("1");            user.setAddress("洛阳");            // 通过SqlSession操作数据库            sqlSession.insert("test.insertUser", user);            //提交事务            sqlSession.commit();        } catch (IOException e) {            e.printStackTrace();        } finally {            // 释放资源            if (sqlSession != null) {                sqlSession.close();            }        }    }

3.8.2 自增主键返回

通过修改sql映射文件,可以将mysql自增主键返回:
添加selectKey实现将主键返回
keyProperty:返回的主键存储在pojo中的哪个属性
order:selectKey的执行顺序,是相对与insert语句来说,由于mysql的自增原理执行完insert语句之后才将主键生成,所以这里selectKey的执行顺序为after
resultType:返回的主键是什么类型
LAST_INSERT_ID():是mysql的函数,返回auto_increment自增列新记录id值。

      <insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">        <!--将插入数据的主键返回 -->        <selectKey keyProperty="id" order="AFTER" resultType="java.long.Integer">            select LAST_INSERT_ID()        </selectKey>        insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})      </insert>

3.8.3 非自增主键返回 (使用uuid())

使用mysql的uuid()函数生成主键,需要修改表中id字段类型为string,长度设置成35位。

执行思路:
先通过uuid()查询到主键,将主键输入 到sql语句中。

执行uuid()语句顺序相对于insert语句之前执行。

    <insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">        <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">            select uuid()        </selectKey>        insert into user(id,username,birthday,sex,address) values(#{id},#{username},#{birthday},#{sex},#{address})    </insert>

通过oracle的序列生成主键:

    <insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">        <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">            SELECT 序列名.nextval()        </selectKey>        insert into user(id,username,birthday,sex,address) values(#{id},#{username},#{birthday},#{sex},#{address})    </insert>

3.9 删除用户

     <!-- 根据id删除用户-->     <delete id="deleteUser" parameterType="java.lang.Integer">        delete from user where id=#{id}     </delete>
    // 根据id删除用户信息    @Test    public void deleteUserTest() {        // mybatis配置文件        String resource = "SqlMapconfig.xml";        SqlSession sqlSession = null;        try {            // 得到配置文件流            InputStream inputStream = Resources.getResourceAsStream(resource);            // 创建会话工厂,传入mybatis的配置文件信息            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);            // 通过工厂得到SqlSession            sqlSession = sqlSessionFactory.openSession();            // 通过SqlSession操作数据库            sqlSession.delete("test.deleteUser", 28);            // 提交事务            sqlSession.commit();        } catch (IOException e) {            e.printStackTrace();        } finally {            // 释放资源            if (sqlSession != null) {                sqlSession.close();            }        }    }

3.10 更新用户

      <!-- 更新用户,User中id不能为空-->     <delete id="updateUser" parameterType="cn.itcast.mybatis.po.User">        update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}     </delete>
    //更新用户信息        @Test        public void updateUserTest() {            // mybatis配置文件            String resource = "SqlMapconfig.xml";            SqlSession sqlSession = null;            try {                // 得到配置文件流                InputStream inputStream = Resources.getResourceAsStream(resource);                // 创建会话工厂,传入mybatis的配置文件信息                SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);                // 通过工厂得到SqlSession                sqlSession = sqlSessionFactory.openSession();                //更新用户对象                User user=new User();                user.setId(27);                user.setUsername("王大军");                user.setBirthday(new Date());                user.setSex("2");                user.setAddress("洛阳");                // 通过SqlSession操作数据库                sqlSession.insert("test.updateUser", user);                //提交事务                sqlSession.commit();            } catch (IOException e) {                e.printStackTrace();            } finally {                // 释放资源                if (sqlSession != null) {                    sqlSession.close();                }            }        }

3.11 mybatis和Hibernate区别

mybatis:不完全是一个ORM(Object Relational Mapping,对象关系映射)框架,需要程序员自己编写Sql语句,不过mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。
可严格控制sql执行性能,灵活度高,灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。
适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。

Hibernate:对象/关系映射能力强,数据库无关性好。学习门槛高,对sql语句进行优化、修改比较困难的,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。
适用与关系模型要求高的软件(例如需求固定的定制化软件),比如:后台管理系统,erp、orm、oa。

4、mybatis开发dao的方法

4.1 需求

将下边的功能实现Dao:
根据用户id查询一个用户信息
根据用户名称模糊查询用户信息列表
添加用户信息

4.2 SqlSession的使用范围

SqlSession中封装了对数据库的操作,如:查询、插入、更新、删除等。
通过SqlSessionFactory创建SqlSession,而SqlSessionFactory是通过SqlSessionFactoryBuilder进行创建。

4.2.1 SqlSessionFactoryBuilder

SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFactoryBuilder了,可以将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量

4.2.2 SqlSessionFactory

SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory。

4.2.3 SqlSession

SqlSession是一个面向用户的接口, sqlSession中定义了数据库操作,默认使用DefaultSqlSession实现类。

执行过程如下:
1、 加载数据源等配置信息
Environment environment = configuration.getEnvironment();
2、 创建数据库链接
3、 创建事务对象
4、 创建Executor,SqlSession所有操作都是通过Executor完成
5、 SqlSession的实现类即DefaultSqlSession,此对象中对操作数据库实质上用的是Executor

结论:
每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
打开一个 SqlSession;使用完毕就要关闭它。通常把这个关闭操作放到 finally 块中以确保每次都能执行关闭。如下:

SqlSession session = sqlSessionFactory.openSession();    try {         // do work    } finally {        session.close();    }

4.3 原始Dao开发方式

原始Dao开发方法需要程序员编写Dao接口和Dao实现类。

4.3.1 映射文件

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- namespace命名空间,用于隔离sql语句,对sql进行分类化管理 --><mapper namespace="test">    <!-- 在映射文件中配置很多sql语句 -->    <!-- 通过id查询用户表的记录 -->    <select id="findUserById" parameterType="int"        resultType="cn.itcast.mybatis.po.User">        SELECT * FROM USER WHERE id=#{value}    </select>    <select id="findUserByName" parameterType="java.lang.String"        resultType="cn.itcast.mybatis.po.User">        select * from user where username like '%${value}%'    </select>    <insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">            select LAST_INSERT_ID()        </selectKey>        insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})    </insert>     <!-- 根据id删除用户-->     <delete id="deleteUser" parameterType="java.lang.Integer">        delete from user where id=#{id}     </delete>      <!-- 更新用户,User中id不能为空-->     <delete id="updateUser" parameterType="cn.itcast.mybatis.po.User">        update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}     </delete></mapper>

4.3.2 Dao接口

public interface UserDao {    //根据id查询用户信息    public User findUserById(int id) throws Exception;    //添加用户信息    public void insertUser(User user) throws Exception;    //删除用户信息    public void deleteUser(int id) throws Exception;}

4.3.3 Dao实现

public class UserDaoImpl implements UserDao{    //需要向dao实现类注入SqlSessionFactory    //这里通过构造方法注入    private SqlSessionFactory sqlSessionFactory;    public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {        this.sqlSessionFactory=sqlSessionFactory;    }    @Override    public User findUserById(int id) throws Exception {        SqlSession sqlSession=sqlSessionFactory.openSession();        User user=null;        try{            user=sqlSession.selectOne("test.findUserById",id);        }finally{            sqlSession.close();//释放资源        }        return user;    }    @Override    public void insertUser(User user) throws Exception {        SqlSession sqlSession=sqlSessionFactory.openSession();        sqlSession.insert("test.insertUser",user);//执行插入操作        sqlSession.commit();//提交事务        sqlSession.close();//释放资源    }    @Override    public void deleteUser(int id) throws Exception {        SqlSession sqlSession=sqlSessionFactory.openSession();        sqlSession.delete("test.deleteUser",id);//执行插入操作        sqlSession.commit();//提交事务        sqlSession.close();//释放资源    }}

4.3.4 测试

public class UserDaoImplTest {    private SqlSessionFactory sqlSessionFactory;    @Before    public void setUp() throws Exception {        //mybatis配置文件        String resource="SqlMapConfig.xml";        //得到配置文件流        InputStream inputStream=Resources.getResourceAsStream(resource);        //创建回话工厂,传入mybatis的配置文件信息        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);     }    @Test    public void testFindUserById() throws Exception {        //创建UserDao的对象        UserDao userDao=new UserDaoImpl(sqlSessionFactory);        //调用UserDao的方法        User user=userDao.findUserById(1);        System.out.println(user);    }}

原始Dao开发中存在以下问题:

Dao方法体存在重复代码:通过SqlSessionFactory创建SqlSession,调用SqlSession的数据库操作方法
调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不得于开发维护。
调用sqlsession方法时传入的变量,由于sqlsession方法使用泛型< Object>,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。

4.4 Mapper动态代理方式

4.4.1 实现原理

Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。

Mapper接口开发需要遵循以下规范:
1、 Mapper.xml(本程序用UserMapper.xml)文件中的namespace与mapper接口的类路径相同。

<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">

2、 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

    <select id="findUserById" parameterType="int"        resultType="cn.itcast.mybatis.po.User">        SELECT * FROM USER WHERE id=#{value}    </select>
package cn.itcast.mybatis.mapper;public interface UserMapper {       //根据id查询用户信息    public User findUserById(int id) throws Exception; }

4.4.2 加载UserMapper.xml文件

修改SqlMapConfig.xml文件:

  <!-- 加载映射文件 -->  <mappers>    <mapper resource="mapper/UserMapper.xml"/>  </mappers>

4.4.3 测试

public class UserMapperTest {    private SqlSessionFactory sqlSessionFactory;    @Before    public void setUp() throws Exception {        //mybatis配置文件        String resource="SqlMapConfig.xml";        //得到配置文件流        InputStream inputStream=Resources.getResourceAsStream(resource);        //创建回话工厂,传入mybatis的配置文件信息        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);     }    @Test    public void testFindUserById() throws Exception {        //获取session        SqlSession sqlsession=sqlSessionFactory.openSession();        //获取mapper接口的代理对象        UserMapper userMapper=sqlsession.getMapper(UserMapper.class);        //调用代理对象方法        User user=userMapper.findUserById(1);        System.out.println(user);        sqlsession.close();    }}

4.4.4 总结

selectOne和selectList
动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。

namespace
mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用pojo包装对象或map对象,保证dao的通用性。
注意:持久层方法的参数可以使用pojo、map等,service方法中建议不要使用包装类型(不利于业务层的可扩展)

5、SqlMapConfig.xml

SqlMapConfig.xml中配置的内容和顺序如下:

properties(属性)settings(全局配置参数)typeAliases(类型别名)typeHandlers(类型处理器)objectFactory(对象工厂)plugins(插件)environments(环境集合属性对象)    environment(环境子属性对象)         transactionManager(事务管理)         dataSource(数据源)mappers(映射器)

5.1 properties(属性)

在classpath下定义db.properties文件

jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/mybatisjdbc.username=rootjdbc.password=root

SqlMapConfig.xml引用如下:

    <!--加载属性文件  -->    <properties resource="db.properties"></properties>    <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>

注意: MyBatis 将按照下面的顺序来加载属性:

 在 properties 元素体内定义的属性首先被读取。
 然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。
 最后读取parameterType传递的属性,它会覆盖已读取的同名属性。

因此,通过parameterType传递的属性具有最高优先级,resource或 url 加载的属性次之,最低优先级的是 properties 元素体内定义的属性。

建议:在properties文件中定义属性名要有一定的特殊性,如:XXX.XXX.XX,以防止被parameterType传递的同名属性无意覆盖。

5.2 settings全局参数配置

mybatis全局配置参数,全局参数将会影响mybatis的运行行为。
这里写图片描述

5.3 ctypeAliases(别名)重点

在mapper.xml中,定义很多的statement,statement需要parameterType指定输入参数的类型、需要resultType指定输出结果的映射类型。

如果在指定类型时输入类型全路径,不方便进行开发,可以针对parameterType或resultType指定的类型定义一些别名,在mapper.xml中通过别名定义,方便开发。

5.3.1 mybatis默认支持别名

 别名        映射的类型_byte       byte _long       long _short      short _int        int _integer    int _double     double _float      float _boolean    boolean string      String byte        Byte long        Long short       Short int         Integer integer     Integer double      Double float       Float boolean     Boolean date        Date decimal     BigDecimal bigdecimal  BigDecimal 

5.3.2 自定义别名

在SqlMapConfig.xml中配置:

<typeAliases>    <!-- 单个别名定义 -->    <typeAlias type="cn.itcast.mybatis.po.User" alias="user"/>    <!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) -->    <package name="cn.itcast.mybatis.po"/>    <package name="其它包"/></typeAliases>

5.4 typeHandlers(类型处理器)

每当MyBatis 设置参数到PreparedStatement 或者从ResultSet 结果集中取得值时,就会使用typeHandler 来处理数据库类型与java 类型之间转换。

<select id="findUserById" parameterType="int" resultType="user">        select * from user where id = #{id}</select>

下表描述了默认的typeHandlers:
这里写图片描述

对于用户自定义的类型,创建一个自定义的类型处理器。参考:http://blog.csdn.net/huey2672/article/details/38151607

5.5 mappers(映射器)

使用相对于类路径的资源如:<mapper resource="sqlmap/User.xml" />
使用完全限定路径如:<mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\User.xml" />
使用mapper接口类路径如:<mapper class="cn.itcast.mybatis.mapper.UserMapper"/>注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
注册指定包下的所有mapper接口如:<package name="cn.itcast.mybatis.mapper"/>注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

6、输入映射

6.1 #{}与${}

#{}实现的是向prepareStatement中的预处理语句中设置参数值,sql语句中#{}表示一个占位符即?。
使用占位符#{}可以有效防止sql注入
在使用时不需要关心参数值的类型,mybatis会自动进行java类型和jdbc类型的转换。
#{}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。

${}和#{}不同,通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换
${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。
使用${}不能防止sql注入,但是有时用${}会非常方便

6.2 传递pojo对象

Mybatis使用ognl表达式解析对象字段的值,如下例子:

<!—传递pojo对象综合查询用户信息 -->    <select id="findUserByUser" parameterType="user" resultType="user">       select * from user where id=#{id} and username like '%${username}%'    </select>

6.3 传递pojo包装对象

开发中通过pojo传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。

6.3.1 定义包装类型pojo

在包装类型的pojo中将复杂的查询条件包装进去。

public class UserQueryVo {//在这里包装所需要的查询条件    //用户查询条件    private UserCustom userCustom;    //还可以包装其他的查询条件,订单,商品等     public UserCustom getUserCustom() {        return userCustom;    }    public void setUserCustom(UserCustom userCustom) {        this.userCustom = userCustom;    }    //其他条件的getter、setter}

6.3.2 mapper.xml

在UserMapper.xml中定义用户信息综合查询

    <!--用户信息综合查询  -->    <select id="findUserList" parameterType="cn.itcast.mybatis.po.UserQueryVo" resultType="cn.itcast.mybatis.po.UserCustom">        select * from user where user.sex=#{userCustom.sex} and user.username like '%${userCustom.username}%'    </select>

6.3.3 mapper.java

UserMapper.java 接口类中

    //用户信息的综合查询    public List<UserCustom> findUserList(UserQueryVo userQueryVo) throws Exception;

6.3.4 测试代码

UserMapperTest.java

// 用户信息的综合查询    @Test    public void testFindUserList() throws Exception {        // 获取session        SqlSession sqlsession = sqlSessionFactory.openSession();        // 获取mapper接口的代理对象        UserMapper userMapper = sqlsession.getMapper(UserMapper.class);        //创建包装对象,设置查询条件        UserQueryVo userQueryVo=new UserQueryVo();        UserCustom userCustom=new UserCustom();        userCustom.setSex("1");        userCustom.setUsername("张");        userQueryVo.setUserCustom(userCustom);        List<UserCustom> list=userMapper.findUserList(userQueryVo);        System.out.println(list);        sqlsession.close();    }

6.4 传递hashmap

Sql映射文件定义如下:

<!-- 传递hashmap综合查询用户信息 -->    <select id="findUserByHashmap" parameterType="hashmap" resultType="user">       select * from user where id=#{id} and username like '%${username}%'    </select>

id和username是hashmap的key

异常测试:
传递的map中的key和sql中解析的key不一致。
测试结果没有报错,只是通过key获取值为空。

7、输出映射

7.1 resultType

可输出简单类型、pojo对象、pojo列表、hashmap

使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。
如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象。
只要查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象,其他属性为相应类型的默认值。

输出pojo对象和输出pojo列表在sql中定义的resultType是一样的。

返回单个pojo对象要保证sql查询出来的结果集为单条,内部使用session.selectOne方法调用,mapper接口使用pojo对象作为方法返回值。
返回pojo列表表示查询出来的结果集可能为多条,内部使用session.selectList方法,mapper接口使用List对象作为方法返回值。

输出pojo对象可以改用hashmap输出类型,将输出的字段名称作为map的key,value为字段值。

7.2 resultMap

resultType可以指定pojo将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名一致方可映射成功。如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系 ,resultMap实质上还需要将查询结果映射到pojo对象中。resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。

7.2.1 定义reusltMap和statement

UserMapper.xml

    <!--定义resultMap -->    <!-- type:resultMap最终映射的java对象类型,可以使用别名          id:对此resultMap的标识     -->    <resultMap type="User" id="userResultMap">        <!-- id表示查询结果集中唯一标识              column:查询出来的列名             property:type指定的pojo类型中对应的属性名        -->        <id column="id_" property="id"/>        <!-- result:查询结果中普通列名 -->        <result column="username_" property="username"/>    </resultMap>    <select id="findUserByIdResultMap" parameterType="int" resultMap="userResultMap">        select id id_,username username_ from user where id=#{value}    </select>

UserMapper.java接口

    //根据id查询用户信息,使用resultMap输出    public User findUserByIdResultMap(int id) throws Exception;

8、动态SQL(重点)

8.1 if 和 where

    <!-- 用户信息综合查询总数-->    <select id="findUserCount" parameterType="cn.itcast.mybatis.po.UserQueryVo" resultType="int">        select count(*) from user        <!-- where可以自动去掉条件中的第一个and -->        <where>            <if test="userCustom!=null">                <if test="userCustom.sex!=null and userCustom.sex!=''">                    and user.sex=#{userCustom.sex}                </if>                <if test="userCustom.username!=null and userCustom.username!=''">                    and user.username like '%${userCustom.username}%'                </if>            </if>        </where>    </select>

8.2 foreach

8.2.1 通过pojo传递list

在pojo中定义list属性ids存储多个用户id,并添加getter/setter方法

public class UserQueryVo {//在这里包装所需要的查询条件    //用户查询条件    private UserCustom userCustom;    //传递多个用户id    private List<Integer> ids;、//getter/setter...}

UserMapper.xml

    <select id="findUsersByIds" parameterType="cn.itcast.mybatis.po.UserQueryVo"  resultType="cn.itcast.mybatis.po.UserCustom">        SELECT * FROM USER         <where>        <if test="ids!=null and ids.size>0">            <foreach collection="ids" item="id" open=" and id in("  close=")"  separator="," >                #{id}            </foreach>        </if>        </where>    </select>

Mapper接口

public List<UserCustom> findUsersByIds(UserQueryVo userQueryVo) throws Exception;

测试代码

    @Test    public void findUsersByIds() throws Exception {        // 获取session        SqlSession sqlsession = sqlSessionFactory.openSession();        // 获取mapper接口的代理对象        UserMapper userMapper = sqlsession.getMapper(UserMapper.class);        List<Integer> ids=new ArrayList<Integer>();        ids.add(1);        ids.add(10);        UserQueryVo userQueryVo=new UserQueryVo();        userQueryVo.setIds(ids);        List<UserCustom> list=userMapper.findUsersByIds(userQueryVo);        System.out.println(list);        sqlsession.close();    }

8.2.2 传递单个List

传递List类型在编写mapper.xml没有区别,唯一不同的是只有一个List参数时它的参数名为list。

UserMapper.xml

    <select id="findUserByList" parameterType="java.util.List" resultType="cn.itcast.mybatis.po.UserCustom">        select * from user         <where>        <!-- 传递List,List中是pojo -->        <if test="list!=null">            <foreach collection="list" item="item" open="and id in(" close=")" separator=",">               #{item.id}             </foreach>        </if>        </where>    </select>

Mapper接口

public List<UserCustom> findUserByList(List userlist) throws Exception;

测试代码

    @Test    public void findUserByList()throws Exception{        //获取session        SqlSession session = sqlSessionFactory.openSession();        //获限mapper接口实例        UserMapper userMapper = session.getMapper(UserMapper.class);        //构造查询条件List        List<User> userlist = new ArrayList<User>();        User user = new User();        user.setId(1);        userlist.add(user);        user = new User();        user.setId(2);        userlist.add(user);         //传递userlist列表查询用户列表        List<UserCustom>list = userMapper.findUserByList(userlist);        //关闭session        session.close();    }

8.2.3 传递单个数组(数组中是pojo)

<!-- 传递数组综合查询用户信息 -->    <select id="selectUserByArray" parameterType="Object[]" resultType="user">        select * from user         <where>        <!-- 传递数组 -->        <if test="array!=null">        <foreach collection="array" index="index" item="item" open="and id in("separator=","close=")">            #{item.id}         </foreach>        </if>        </where>    </select>

sql只接收一个数组参数,这时sql解析参数的名称mybatis固定为array,如果数组是通过一个pojo传递到sql则参数的名称为pojo中的属性名。

index:为数组的下标。
item:为数组每个元素的名称,名称随意定义
open:循环开始
close:循环结束
separator:中间分隔输出

8.2.4 传递单个数组(数组中是字符串类型)

<!-- 传递数组综合查询用户信息 -->    <select id="selectUserByArray" parameterType="Object[]" resultType="user">        select * from user         <where>        <!-- 传递数组 -->        <if test="array!=null">        <foreach collection="array" index="index" item="item" open="and id in(" separator="," close=")">            #{item}         </foreach>        </if>        </where>    </select>

如果数组中是简单类型则写为#{item},不用再通过ognl获取对象属性值了。

8.3 sql片段

Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的,如下:

<!-- 传递pojo综合查询用户信息 -->    <select id="findUserList" parameterType="user" resultType="user">        select * from user         <where>        <if test="id!=null and id!=''">        and id=#{id}        </if>        <if test="username!=null and username!=''">        and username like '%${username}%'        </if>        </where>    </select>

将where条件抽取出来:

<sql id="query_user_where">    <if test="id!=null and id!=''">        and id=#{id}    </if>    <if test="username!=null and username!=''">        and username like '%${username}%'    </if></sql>

使用include引用:

<select id="findUserList" parameterType="user" resultType="user">        select * from user         <where>        <include refid="query_user_where"/>        </where></select>

注意:如果引用其它mapper.xml的sql片段,则在引用时需要加上namespace,如下:

0 1