MyBatis入门第2天--MyBatis基础知识(二)

41 篇文章 1 订阅
12 篇文章 1 订阅
文档版本开发工具测试平台工程名字日期作者备注
V1.02016.06.26lutianfeinone

mybatis开发dao的方法

SqlSession使用范围
  • SqlSessionFactoryBuilder

    • 通过SqlSessionFactoryBuilder创建会话工厂SqlSessionFactory
    • 将SqlSessionFactoryBuilder当成一个工具类使用即可,不需要使用单例管理SqlSessionFactoryBuilder。
    • 在需要创建SqlSessionFactory时候,只需要new一次SqlSessionFactoryBuilder即可。
  • SqlSessionFactory

    • 通过SqlSessionFactory创建SqlSession,使用单例模式管理sqlSessionFactory(工厂一旦创建,使用一个实例)。
    • 将来mybatis和spring整合后,使用单例模式管理sqlSessionFactory。
  • SqlSession

    • SqlSession是一个面向用户(程序员)的接口。
    • SqlSession中提供了很多操作数据库的方法:如:selectOne(返回单个对象)、selectList(返回单个或多个对象)、。
    • SqlSession是线程不安全的,在SqlSesion实现类中除了有接口中的方法(操作数据库的方法)还有数据域属性。
    • SqlSession最佳应用场合在方法体内,定义成局部变量使用。
  • SqlSession执行过程如下:

    • 1、 加载数据源等配置信息
      • Environment environment = configuration.getEnvironment();
    • 2、 创建数据库链接
    • 3、 创建事务对象
    • 4、 创建Executor,SqlSession所有操作都是通过Executor完成,mybatis源码如下:
    if (ExecutorType.BATCH == executorType) {
          executor = newBatchExecutor(this, transaction);
        } elseif (ExecutorType.REUSE == executorType) {
          executor = new ReuseExecutor(this, transaction);
        } else {
          executor = new SimpleExecutor(this, transaction);
        }
    if (cacheEnabled) {
          executor = new CachingExecutor(executor, autoCommit);
        }
* 5、 SqlSession的实现类即DefaultSqlSession,此对象中对操作数据库实质上用的是Executor
  • 结论:
    • 每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
    • 打开一个 SqlSession;使用完毕就要关闭它。通常把这个关闭操作放到 finally 块中以确保每次都能执行关闭。如下:
SqlSession session = sqlSessionFactory.openSession();
    try {
          // do work
    } finally {
          session.close();
    }


原始dao开发方法
  • 程序员需要写dao接口和dao实现类
  • 思路

    • 程序员需要写dao接口和dao实现类。
    • 需要向dao实现类中注入SqlSessionFactory,在方法体内通过SqlSessionFactory创建SqlSession
  • dao接口


  • 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 = sqlSession.selectOne("test.findUserById", id);

        // 释放资源
        sqlSession.close();

        return user;

    }

    @Override
    public List<User> findUserByName(String name) throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();

        List<User> list = sqlSession.selectList("test.findUserByName", name);

        // 释放资源
        sqlSession.close();

        return list;
    }


    @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();

    }


  • 测试代码:
public class UserDaoImplTest {

    private SqlSessionFactory sqlSessionFactory;

    // 此方法是在执行testFindUserById之前执行
    @Before
    public void setUp() throws Exception {
        // 创建sqlSessionFactory

        // 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接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作量。
  • 调用sqlsession方法时将statement的id硬编码了
  • 调用sqlsession方法时传入的变量,由于sqlsession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。

mapper代理方法

  • 程序员只需要mapper接口(相当 于dao接口)
mapper代理开发规范
  • 程序员还需要编写mapper.xml映射文件
  • 程序员编写mapper接口需要遵循一些开发规范,这样Mybatis可以自动生成mapper接口实现类代理对象。

  • 开发规范:

    • 1、在mapper.xml中namespace等于mapper接口地址
      • 2、mapper.java接口中的方法名和mapper.xml中statement的id一致
    • 3、mapper.java接口中的方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致。
    • 4、mapper.java接口中的方法返回值类型和mapper.xml中statement的resultType指定的类型一致。

  • 总结:
    • 以上开发规范主要是对下边的代码进行统一生成:
User user = sqlSession.selectOne("test.findUserById", id);
sqlSession.insert("test.insertUser", user);
  • 编写XXXmapper.java
public interface UserMapper {

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

    //用户信息综合查询总数
    public int findUserCount(UserQueryVo userQueryVo) throws Exception;

    //根据id查询用户信息
    public User findUserById(int id) throws Exception;

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


    //根据用户名列查询用户列表
    public List<User> findUserByName(String name)throws Exception;

    //插入用户
    public void insertUser(User user)throws Exception;

    //删除用户
    public void deleteUser(int id)throws Exception;

}


  • XXXmapper.xml



  • 在SqlMapConfig.xml中加载mapper.xml
    <!-- 加载 映射文件 -->
    <mappers>
         <mapper class="cn.itcast.mybatis.mapper.UserMapper"/>
        <!-- 批量加载mapper
        指定mapper接口的包名,mybatis自动扫描包下边所有mapper接口进行加载,遵循一些规范:需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在一个目录中
        上边规范的前提是:使用的是mapper代理方法
         -->
        <package name="cn.itcast.mybatis.mapper"/>

    </mappers>
  • 测试
    @Test
    public void testFindUserById() throws Exception {

        SqlSession sqlSession = sqlSessionFactory.openSession();

        //创建UserMapper对象,mybatis自动生成mapper代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        //调用userMapper的方法

        User user = userMapper.findUserById(1);

        System.out.println(user);


    }
  • 一些问题总结
    • 代理对象内部调用selectOne或selectList ?
      • 如果mapper方法返回单个pojo对象(非集合对象),代理对象内部通过selectOne查询数据库。
      • 如果mapper方法返回集合对象,代理对象内部通过selectList查询数据库。
    • mapper接口方法参数只能有一个是否影响系统扩展维护?
      • 系统框架中,dao层的代码是被业务层公用的。即使mapper接口只有一个参数,可以使用包装类型的pojo满足不同的业务方法的需求。
      • 注意:持久层方法的参数可以包装类型、map、list等等;
      • 但service方法中建议不要使用包装类型(不利于业务层的可扩展)。


SqlMapConfig.xml配置文件
  • mybatis的全局配置文件SqlMapConfig.xml,配置内容如下:
    • properties(属性)
    • settings(全局配置参数)
    • typeAliases(类型别名)
    • typeHandlers(类型处理器)
    • objectFactory(对象工厂)
    • plugins(插件)
    • environments(环境集合属性对象)
    • environment(环境子属性对象)
    • transactionManager(事务管理)
    • dataSource(数据源)
    • mappers(映射器)
properties属性
  • 需求:将数据库连接参数单独配置在db.properties中,只需要在SqlMapConfig.xml中加载db.properties的属性值。

  • 在SqlMapConfig.xml中就不需要对数据库连接参数硬编码。

  • 将数据库连接参数只配置在db.properties中:方便对参数进行统一管理,其它xml可以引用该db.properties。
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=mysql


  • 在sqlMapConfig.xml加载属性文件
<!-- 加载属性文件 -->
    <properties resource="db.properties">
        <!--properties中还可以配置一些属性名和属性值  -->
        <!-- <property name="jdbc.driver" value=""/> -->
    </properties>

    <!-- 和spring整合后 environments配置将废除-->
    <environments default="development">
        <environment id="development">
        <!-- 使用jdbc事务管理,事务控制由mybatis-->
            <transactionManager type="JDBC" />
        <!-- 数据库连接池,由mybatis管理-->
            <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>
  • properties特性:
  • 注意: MyBatis 将按照下面的顺序来加载属性:
    • 在 properties 元素体内定义的属性首先被读取。
    • 然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。
    • 最后读取parameterType传递的属性,它会覆盖已读取的同名属性。
  • 因此,通过parameterType传递的属性具有最高优先级resourceurl 加载的属性次之最低优先级的是 properties 元素体内定义的属性。

  • 建议:

    • 不要在properties元素体内添加任何属性值,只将属性值定义在properties文件中。
    • 在properties文件中定义属性名要有一定的特殊性,如:XXXXX.XXXXX.XXXX


settings全局参数配置
  • mybatis框架在运行时可以调整一些运行参数。
  • 比如:开启二级缓存、开启延迟加载。。
  • 全局参数将会影响mybatis的运行行为。
  • 详细参见“学习资料/mybatis-settings.xlsx”文件


typeAliases(别名)重点
  • 需求

    • 在mapper.xml中,定义很多的statement,statement需要parameterType指定输入参数的类型、需要resultType指定输出结果的映射类型。
    • 如果在指定类型时输入类型全路径,不方便进行开发,可以针对parameterTyperesultType 指定的类型定义一些别名,在mapper.xml中通过别名定义,方便开发。
  • mybatis默认支持的别名

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal


  • 自定义别名
  • 单个别名定义

  • 引用别名:

  • 批量定义别名(常用)

<!-- 别名定义 -->
    <typeAliases>

        <!-- 针对单个别名定义
        type:类型的路径
        alias:别名
         -->
        <!-- <typeAlias type="cn.itcast.mybatis.po.User" alias="user"/> -->
        <!-- 批量别名定义 
        指定包名,mybatis自动扫描包中的po类,自动定义别名,别名就是类名(首字母大写或小写都可以)
        -->
        <package name="cn.itcast.mybatis.po"/>

    </typeAliases>


typeHandlers(类型处理器)
  • mybatis中通过typeHandlers完成jdbc类型和java类型的转换。
  • 通常情况下,mybatis提供的类型处理器满足日常需要,不需要自定义
  • mybatis支持类型处理器:
类型处理器Java类型JDBC类型
BooleanTypeHandlerBoolean,boolean任何兼容的布尔值
ByteTypeHandlerByte,byte任何兼容的数字或字节类型
ShortTypeHandlerShort,short任何兼容的数字或短整型
IntegerTypeHandlerInteger,int任何兼容的数字和整型
LongTypeHandlerLong,long任何兼容的数字或长整型
FloatTypeHandlerFloat,float任何兼容的数字或单精度浮点型
DoubleTypeHandlerDouble,double任何兼容的数字或双精度浮点型
BigDecimalTypeHandlerBigDecimal任何兼容的数字或十进制小数类型
StringTypeHandlerStringCHAR和VARCHAR类型
ClobTypeHandlerStringCLOB和LONGVARCHAR类型
NStringTypeHandlerStringNVARCHAR和NCHAR类型
NClobTypeHandlerStringNCLOB类型
ByteArrayTypeHandlerbyte[]任何兼容的字节流类型
BlobTypeHandlerbyte[]BLOB和LONGVARBINARY类型
DateTypeHandlerDate(java.util)TIMESTAMP类型
DateOnlyTypeHandlerDate(java.util)DATE类型
TimeOnlyTypeHandlerDate(java.util)TIME类型
SqlTimestampTypeHandlerTimestamp(java.sql)TIMESTAMP类型
SqlDateTypeHandlerDate(java.sql)DATE类型
SqlTimeTypeHandlerTime(java.sql)TIME类型
ObjectTypeHandler任意其他或未指定类型
EnumTypeHandlerEnumeration类型VARCHAR-任何兼容的字符串类型,作为代码存储(而不是索引)。


mappers(映射配置)
  • 通过resource加载单个映射文件,<mapper resource=" " />使用相对于类路径的资源
    • 如:<mapper resource="sqlmap/User.xml" />


  • 使用完全限定路径<mapper url=" " />

    • 如:<mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\User.xml" />
  • 通过mapper接口加载单个mapper

    • 规范:mapper**接口类名和mapper.xml映射文件名称必须保持一致,且在一个package目录中**;并且要使用mapper代理方法
<!-- 通过mapper接口加载单个映射文件需要遵循一些规范:mapper接口类名和mapper.xml映射文件名称必须保持一致,且在一个package目录中;并且要使用mapper代理方法-->
    <mapper class="cn.itcast.mybatis.mapper.UserMapper"/>


  • 批量加载mapper(推荐使用)
    • 当指定mapper接口的包名时,mybatis会自动扫描包下边所有mapper接口进行加载
    • 应遵循的一些规范:需要将mapper**接口类名和mapper.xml映射文件名称保持一致,且在一个package目录中;并且使用的是**mapper代理方法
<package name="cn.itcast.mybatis.mapper"/>


输入映射
  • 通过parameterType指定输入参数的类型,类型可以是简单类型、hashmap、pojo的包装类型。
传递pojo的包装对象
  • 需求:

    • 完成用户信息的综合查询,需要传入查询条件很复杂(可能包括用户信息、其它信息,比如商品、订单的)
  • 定义包装类型pojo

    • 针对上边需求,建议使用自定义的包装类型的pojo。在包装类型的pojo中将复杂的查询条件包装进去。


  • 映射文件XXXmapper.xml设置
    • 在UserMapper.xml中定义用户信息综合查询(查询条件复杂,通过高级查询进行复杂关联查询)。

  • mapper.java

  • 测试代码


传递hashmap
  • Sql映射文件定义如下:
    • #中 id 和 username是hashmap的key。
<!-- 传递<u>hashmap</u>综合查询用户信息 -->
    <select id="findUserByHashmap" parameterType="hashmap" resultType="user">

       select * from user where id=#{id} and username like '%${username}%'

    </select>


  • 测试:
Public void testFindUserByHashmap()throws Exception{
        //获取session
        SqlSession session = sqlSessionFactory.openSession();
        //获限mapper接口实例
        UserMapper userMapper = session.getMapper(UserMapper.class);
        //构造查询条件Hashmap对象
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("id", 1);
        map.put("username", "管理员");

        //传递Hashmap对象查询用户列表
        List<User>list = userMapper.findUserByHashmap(map);
        //关闭session
        session.close();
    }
  • 异常测试:
    • 传递的map中的key和sql中解析的key不一致。
    • 测试结果没有报错,只是通过key获取值为空。


输出映射
resultType
  • 使用resultType进行输出映射,只有查询出来的列名pojo中的属性名一致,该列才可以映射成功。

    • 如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象。
    • 只要查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象。
  • 输出简单类型

  • 需求

    • 用户信息的综合查询列表总数,通过查询总数和上边用户综合查询列表才可以实现分页。
  • Usermapper.xml


  • Usermapper.java

  • 测试代码

  • 小结

    • 查询出来的结果集只有一行且一列,可以使用简单类型进行输出映射。
  • 输出pojo对象和pojo列表

  • 不管是输出的pojo单个对象还是一个列表(list中包括pojo),在mapper.xml中resultType指定的类型是一样的。
  • 在mapper.java指定的方法返回值类型不一样:

    • 1、输出单个pojo对象,方法返回值是单个对象类型
    • 2、输出pojo对象list,方法返回值是List<Pojo>
  • 生成的动态代理对象中是根据mapper方法的返回值类型确定是调用selectOne(返回单个对象调用)还是selectList (返回集合对象调用)


resultMap
  • mybatis中使用resultMap完成高级输出结果映射。

  • resultMap使用方法

    • 如果查询出来的列名pojo的属性名不一致,通过定义一个resultMap列名和pojo属性名之间作一个映射关系
  • 案例: 将下边的sql使用User完成映射

    • User类中属性名和上边查询列名不一致。
SELECT id id_,username username_ FROM USER WHERE id=#{value}


  • 1、在UserMapper.xml中定义resultMap

<!-- 定义resultMap
    将SELECT id id_,username username_ FROM USER 和User类中的属性作一个映射关系

    type:resultMap最终映射的java对象类型,可以使用别名
    id:对resultMap的唯一标识
-->
     <resultMap type="user" id="userResultMap">
         <!-- id表示查询结果集中唯一标识 
         column:查询出来的列名
         property:type指定的pojo类型中的属性名
         最终resultMap对column和property作一个映射关系 (对应关系)
         -->
         <id column="id_" property="id"/>
         <!-- 
         result:对普通名映射定义
         column:查询出来的列名
         property:type指定的pojo类型中的属性名
         最终resultMap对column和property作一个映射关系 (对应关系)
          -->
         <result column="username_" property="username"/>

     </resultMap>



  • 2、使用resultMap作为statement的输出映射类型
    <!-- 使用resultMap进行输出映射
    resultMap:指定定义的resultMap的id,如果这个resultMap在其它的mapper文件,前边需要加namespace
    -->
    <select id="findUserByIdResultMap" parameterType="int" resultMap="userResultMap">
        SELECT id id_,username username_ FROM USER WHERE id=#{value}
    </select>


  • mapper.java

  • 测试

    @Test
    public void testFindUserByIdResultMap() throws Exception {

        SqlSession sqlSession = sqlSessionFactory.openSession();

        //创建UserMapper对象,mybatis自动生成mapper代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        //调用userMapper的方法

        User user = userMapper.findUserByIdResultMap(1);

        System.out.println(user);


    }


  • 小结
    • 使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。
    • 如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。
动态sql
  • 什么是动态sql

    • mybatis核心 对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接、组装。
  • 需求

    • 用户信息综合查询列表和用户信息查询列表总数这两个statement的定义使用动态sql。
    • 对查询条件进行判断,如果输入参数不为空才进行查询条件拼接。
  • mapper.xml


  • 测试代码


sql片段
  • 需求

    • 将上边实现的动态sql判断代码块抽取出来,组成一个sql片段。其它的statement中就可以引用sql片段。方便程序员进行开发。
  • 定义sql片段,在UserMapper.xml顶部

  • 引用sql片段
    • 在mapper.xml中定义的statement中引用sql片段:


foreach
  • 向sql传递数组或List,mybatis使用foreach解析

  • 需求

    • 在用户查询列表和查询总数的statement中增加多个id输入查询。sql语句如下:
//两种方法:
SELECT * FROM USER WHERE id=1 OR id=10 OR id=16
SELECT * FROM USER WHERE id IN(1,10,16)
  • 在输入参数类型中添加List<Integer> ids传入多个id


  • 修改mapper.xml
    • 对于此类型Sql语句:WHERE id=1 OR id=10 OR id=16
    • 在查询条件中,查询条件定义成一个sql片段,需要修改sql片段。
    <sql id="query_user_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 test="ids!=null">
            <!-- 使用 foreach遍历传入ids
            collection:指定输入 对象中集合属性
            item:每个遍历生成对象中
            open:开始遍历时拼接的串
            close:结束遍历时拼接的串
            separator:遍历的两个对象中需要拼接的串
             -->
             <!-- 使用实现下边的sql拼接:
              AND (id=1 OR id=10 OR id=16) 
              -->
            <foreach collection="ids" item="user_id" open="AND (" close=")" separator="or">
                <!-- 每个遍历需要拼接的串 -->
                id=#{user_id}
            </foreach>

            <!-- 实现  “ and id IN(1,10,16)”拼接 -->
            <!-- <foreach collection="ids" item="user_id" open="and id IN(" close=")" separator=",">
                每个遍历需要拼接的串
                #{user_id}
            </foreach> -->

            </if>
        </if>
    </sql>

  • 测试代码

  • 在UserQueryVo中:

public class UserQueryVo {

    //传入多个id
    private List<Integer> ids;

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }
}


  • 对于另外一个sql的实现:
<!-- 实现  “ and id IN(1,10,16)”拼接 -->
<!-- <foreach collection="ids" item="user_id" open="and id IN(" close=")" separator=",">
    每个遍历需要拼接的串
    #{user_id}
</foreach> -->
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值