Mybatis

原始jdbc开发存在的问题如下:
数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能【资源浪费】
sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。【耦合】
查询操作时,需要手动将结果集中的数据手动封装到实体中。插入操作时,需要手动将实体的数据设置到sql语句的占位
符位置【new Bean对象 使用set方法添加到Bean对象中】
应对上述问题给出的解决方案:
使用数据库连接池初始化连接资源【解决资源浪费】
②将sql语句抽取到xml配置文件中【解耦合】
③使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射【使用一些方法自动将数据给Bean对象】

概念:

1.持久层的框架
2.隐藏jdbc繁杂的api,进行响应的封装。
3.只用关心sql语句。
3.实体对象和数据库表单的映射关系。

userMapper

<?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="userMapper">
    <!--查询操作-->
    <select id="findAll" resultType="com.xjbt.domain.User">
        select * from user
    </select>
    <!--增加操作-->
    <insert id="save" parameterType="com.xjbt.domain.User">
        insert into user values(#{id},#{username},#{password})
    </insert>
    <!--修改操作-->
    <insert id="update" parameterType="com.xjbt.domain.User">
        update user set username=#{username},password=#{password} where id=#{id}
    </insert>
    <!--删除操作-->
    <delete id="delete" parameterType="java.lang.Integer">
        delete from user where id=#{id}
    </delete>
</mapper>

test

public class MyBatisTest {
    @Test
    public void test1() throws IOException {
        //获得核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        //获得session工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        //获得session回话对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //执行操作  参数:namespace+id
        List<User> userList = sqlSession.selectList("userMapper.findAll");
        //打印数据
        System.out.println(userList);
        //释放资源
        sqlSession.close();
    }

   @Test
        sqlSession.insert("userMapper.save",user);
        //事务提交
        sqlSession.commit();
    

    @Test
        sqlSession.update("userMapper.update",user);
        //事务提交
        sqlSession.commit();
  

    @Test
        sqlSession.update("userMapper.delete",6);
        //事务提交
        sqlSession.commit();

sqlMapConfig

<?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"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <!--加载映射文件-->
    <mappers>
        <mapper resource="com/xjbt/mapper/UserMapper.xml"></mapper>
    </mappers>
</configuration>
MyBatis核心配置文件概述

MyBatis常用配置解析

  • environments标签:
    事务管理器(transactionManager)类型 (type) 有两种: <transactionManager type="JDBC"></transactionManager>
    JDBC:这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
    MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE应用服务器的上下文)。默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置为false来阻止它默认的关闭行为。
    数据源(dataSource)类型(type)有三种:

    <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
    </dataSource>
    

    UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
    POOLED:这种数据源的实现利用“池”的概念将JDBC连接对象组织起来。
    JNDI:这个数据源的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。

  • mapper标签:放在mappers标签内
    该标签的作用是加载映射的,加载方式有如下几种:
    使用相对于类路径的资源引用,例如: <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
    使用完全限定资源定位符(URL),例如: <mapper url="file://var/mappers/AuthorMapper.xml">
    使用映射器接口实现类的完全限定类名,例如:<mapper class="org.mybatis.builder.AuthorMapper"/>
    将包内的映射器接口实现全部主册为映射器,例如: <package name="org.mybatis.builder"/>

  • Properties标签: <properties resource="jdbc.properties" />
    实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件

  • typeAliases标签:类型别名是为Java类型设置一个短的名字 顺序要放在properties标签后面

       <typeAliases>
            <typeAlias type="com.xjbt.domain.User" alias="user"></typeAlias>
       </typeAliases>
    
  • typeHandlers标签:
    你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。
    具体做法为:实现org.apache.ibatis.type.TypeHandler接口,或继承一个很便利的类org.apache.ibatis.type.BaseTypeHandler,然
    后可以选择性地将它映射到一个JDBC类型。
    例如需求:一个Java中的Date数据类型,我想将之存到数据库的时候存成时间戳,取出来时转换成java的Date,即java的Date与数据库的bigint毫秒值之间转换。
    开发步骤:
    ①定义转换类继承类BaseTypeHandler<T>
    ②覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时 mysql的字符串类型转换成java的Type类型的方法

public class DateTypeHandler extends BaseTypeHandler<Date> {
    //将java类型转换成数据库的数据类型
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
        long time = date.getTime();//毫秒值
        preparedStatement.setLong(i,time);
    }

    /**
     * 将数据库的类型转换成java类型
     * @param resultSet   转换后的结果集
     * @param s  数据库要转的字段
     * @return
     * @throws SQLException
     */
    public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
        long aLong = resultSet.getLong(s);
        Date date=new Date(aLong);
        return date;
    }
    //将数据库的类型转换成java类型
    public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
        long aLong = resultSet.getLong(i);
        Date date=new Date(aLong);
        return date;
    }
    //将数据库的类型转换成java类型
    public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        long aLong = callableStatement.getLong(i);
        Date date=new Date(aLong);
        return date;
    }
}

③在MyBatis核心配置文件中进行注册

 <!--注册类型转换器-->
    <typeHandlers>
        <typeHandler handler="com.xjbt.handler.DateTypeHandler"/>
    </typeHandlers>

④测试转换是否正确

 /**
     * 类型转换:插入数据库将date存成bigInt类型
     * @throws IOException
     */
    @Test
    public void testIf1() throws IOException {
        InputStream resource = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user=new User();
        user.setUsername("ceshi");
        user.setPassword("123");
        user.setBirthday(new Date());
        mapper.insertUser(user);
        sqlSession.commit();
        sqlSession.close();
    }

    /**
     * 类型转换:获取 从数据库查询出的时间戳转换成Date类型
     * @throws IOException
     */
    @Test
    public void testIf2() throws IOException {
        InputStream resource = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user=new User();
        List<User> all = mapper.findAll(user);
        System.out.println(all);
    }
  • plugins标签:
    MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据
    开发步骤:
    导入通用PageHelper的坐标
<!--mybatis分页-->
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>3.7.5</version>
    </dependency>
    <dependency>
      <groupId>com.github.jsqlparser</groupId>
      <artifactId>jsqlparser</artifactId>
      <version>0.9.1</version>
    </dependency>

在mybatis核心配置文件中配置

 <!--配置分页助手插件-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql"/><!--使用那种数据库语言 例如:mysql使用的是limit-->
        </plugin>
    </plugins>

PageHelper插件测试分页数据获取

    @Test
    public void testIf2() throws IOException {
        InputStream resource = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user=new User();
        PageHelper.startPage(1,3);
        List<User> all = mapper.findAll(user);
        for (User u:all) {
            System.out.println(u);
        }
 //获得与分页相关参数
        PageInfo<User> pageInfo = new PageInfo<User>(all);
        System.out.println("当前页:"+pageInfo.getPageNum());
        System.out.println("每页显示条数:"+pageInfo.getPageSize());
        System.out.println("总条数:"+pageInfo.getTotal());
        System.out.println("总页数:"+pageInfo.getPages());
        System.out.println("上一页:"+pageInfo.getPrePage());
        System.out.println("下一页:"+pageInfo.getNextPage());
        System.out.println("是否是第一个:"+pageInfo.isIsFirstPage());
        System.out.println("是否是最后一个: "+pageInfo.isIsLastPage());
    }
代理开发方式

代理开发方式介绍:可不写dao层接口的实现类,mybatis动态代理对象,实现这个接口,相当与这个实现类。
Mapper接口开发需要遵循以下规范:
1、Mapper.xml文件中的namespace与mapper接口的全限定名相同
2、Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
4、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同


dao层UserMapper接口

public interface UserMapper {
    public User findById(int id);
}

UserMapper.xml

<mapper namespace="com.xjbt.dao.UserMapper">
    <!--查询操作-->
    <select id="findById" parameterType="int" resultType="user">
        select * from user where id = #{id}
    </select>
</mapper>

serviceDemo测试

public class ServiceDemo  {
    public static void main(String[] args) throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sessionFactory.openSession();
        //mybatis动态实现dao层UserMapper接口的实现类
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.findById(1);
        System.out.println(user);
    }
}
动态sql:
  1. 动态SQL之<if>
    <select id="findAll" resultType="user" parameterType="user">
        select * from user
        <where>
            <if test="id!=0">
                and id = #{id}
            </if>
            <if test="username!=null">
                and username = #{username}
            </if>
            <if test="password!=null">
                and password = #{password}
            </if>
        </where>

    </select>
  1. 动态sql <froeach>
    <select id="findByIds" parameterType="list" resultType="user">
        select * from user
        <where>
            <foreach collection="list" open="id in (" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>
  1. 代码片段
<!--代码片段-->
 <sql id="selector">   
         select * from user
    </sql>
    <select id="findByIds" parameterType="list" resultType="user">
        <!--代码片段引用-->
        <include refid="selector"></include>
        <where>
            <foreach collection="list" open="id in (" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>
Mybatis多表查询
  • 一对一查询
    一对一查询的模型:
    用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
    一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户
<mapper namespace="com.xjbt.dao.OrderMapper">
<resultMap id="orderMap" type="order">
    <!--
        column 字段名称
        property 对象的属性名称
    -->
    <id column="oid" property="id" />
    <result column="ordertime" property="datetime" />
    <result column="total" property="total"/>
    <!--<result column="uid" property="user.id"/>
    <result column="username" property="user.username"/>
    <result column="password" property="user.password"/>
    <result column="birthday" property="user.birthday"/>-->

    <!--
    property order的属性名
    javatype property的类型 这里user代表全限定名称的别名【已经在sqlMapConfig.xml中配置别名】
    -->
    <association property="user" javaType="user">
        <id column="uid" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="birthday" property="birthday"/>
    </association>
</resultMap>
    
    <select id="findAll" resultMap="orderMap">
        select *,o.id oid from orders o,user u WHERE o.uid=u.id;
    </select>
</mapper>
  • 一对多查询
    一对多查询的模型:
    用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户一对多
    查询的需求:查询一个用户,与此同时查询出该用户具有的订单
<mapper namespace="com.xjbt.dao.UserMapper">
    <!--查询操作-->
    <resultMap id="userMap" type="user">
        <id column="uid" property="id" />
        <result column="username" property="username" />
        <result column="birthday" property="birthday" />
        <collection property="orderList" ofType="order">
           <result column="ordertime" property="datetime" />
            <result property="total" column="total" />
        </collection>
    </resultMap>

   <select id="findAll" resultMap="userMap">
        select * ,o.id oid from user u,orders o where u.id=o.uid;
   </select>
</mapper>
  • 多对多查询
    多对多查询的模型:
    用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用多对多查询的
    需求:查询用户同时查询出该用户的所有角色
 <resultMap id="userAndRole" type="user">
        <id column="id" property="id" />
        <result column="username" property="username" />
        <result column="birthday" property="birthday" />
        <collection property="roleList" ofType="role">
            <id column="role_id" property="id" />
            <result column="rolename" property="rolename" />
        </collection>
    </resultMap>
    <select id="findUserAndRoleAll" resultMap="userAndRole">
            SELECT * from user u left join (select * from user_role ur,role r where r.id=ur.role_Id) urr on u.id=urr.user_id

    </select>
MyBatis的常用注解

@lnsert:实现新增
@Update:实现更新
@Delete:实现删除@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result一起使用,封装多个结果集@One:实现一对一结果集封装
@Many:实现一对多结果集封装

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容