MyBatis

简介
  • 基于Java的持久层框架,内部封装JDBC,只需关注sql语句本身
  • 通过xml或注解的方式,将要执行的各种statement配置起来
  • 执行sql语句并将结果映射为Java对象并返回
快速入门
  1. 添加坐标

    • mysql-connector-java
    • mybatis
  2. 创建user表

  3. 编写user实体类

  4. 编写映射文件UserMapper.xml

    <?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命名空间-->
    <mapper namespace="userMapper">
        <!--select根标签可选的还有insert、delete、update-->
        <!--id语句的标识,与命名空间一起组成查询的标识-->
        <!--resultType查询结果对应的实体类型-->
        <select id="findAll" resultType="domain.User">
            <!--要执行的sql语句-->
            select * from user
        </select>
    </mapper>
  5. 编写核心文件SqlMapConfig.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="develop">
            <environment id="develop">
                <transactionManager type="JDBC"></transactionManager>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/login?serverTimezone=Asia/Shanghai&amp;characterEncoding=utf8"/>
                    <property name="username" value="login_root"/>
                    <property name="password" value="yy947599"/>
                </dataSource>
            </environment>
        </environments>
        <!--加载映射文件-->
        <mappers>
            <mapper resource="mapper/UserMapper.xml" />
        </mappers>
    </configuration>
  6. 编写测试类

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

注意:Mybatis默认是事务不提交的

  • insert:API为insert

    <!--parameterType插入的参数类型-->
    <insert id="save" parameterType="domain.User">
      <!--占位符:#{实体属性名}-->  
        insert into user values(#{id},#{username},#{password})
    </insert>
    public class MyBatisTest {
        @Test
        public void test2() throws IOException {
            //模拟user对象
            User user = new User();
            user.setUsername("tom");
            user.setPassword("124");
            //获得核心配置文件
            InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml");
            //获得工厂对象
            SqlSessionFactory sql = new SqlSessionFactoryBuilder().build(stream);
            //获得session回话对象
            SqlSession sqlSession = sql.openSession();
            //执行操作,参数:namespace.id,实体类
            sqlSession.insert("userMapper.save",user);
            //Mybatis默认是事务不提交的
            //提交事务
            sqlSession.commit();
            //释放资源
            sqlSession.close();
        }
    }
  • update:API为update

    <!--parameterType插入的参数类型-->
    <update id="up" parameterType="domain.User">
        <!--占位符:#{实体属性名}-->  
        update user set username=#{username},password=#{password} where id=#{id}
    </update>
    public class MyBatisTest {
        @Test
        public void test2() throws IOException {
            //模拟user对象
            User user = new User();
            user.setId(3);
            user.setUsername("lucy");
            user.setPassword("124");
            //获得核心配置文件
            InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml");
            //获得工厂对象
            SqlSessionFactory sql = new SqlSessionFactoryBuilder().build(stream);
            //获得session回话对象
            SqlSession sqlSession = sql.openSession();
            //执行操作,参数:namespace.id,实体类
            sqlSession.update("userMapper.up",user);
            //Mybatis默认是事务不提交的
            //提交事务
            sqlSession.commit();
            //释放资源
            sqlSession.close();
        }
    }
  • delete:API为update

    <!--parameterType插入的参数类型-->
    <delete id="del" parameterType="java.lang.Integer">
       <!--占位符:#{实体属性名}-->  
       delete from user where id=#{id}
    </delete>
    public class MyBatisTest {
        @Test
        public void test2() throws IOException {
            //获得核心配置文件
            InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml");
            //获得工厂对象
            SqlSessionFactory sql = new SqlSessionFactoryBuilder().build(stream);
            //获得session回话对象
            SqlSession sqlSession = sql.openSession();
            //执行操作,参数:namespace.id,实体类
            sqlSession.update("userMapper.del",3);
            //Mybatis默认是事务不提交的
            //提交事务
            sqlSession.commit();
            //释放资源
            sqlSession.close();
        }
    }
environments标签

数据库环境的配置,支持多环境配置

<!--default:指定默认的环境名称-->
<environments default="develop">
<!--id:指定当前环境名称-->
<environment id="develop">
    <!--type:指定事务管理类型是JDBC-->
    <transactionManager type="JDBC" />
    <!--type:指定当前数据源类型是连接池-->
    <dataSource type="POOLED">
        <!--数据源基本配置参数-->
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/login?serverTimezone=Asia/Shanghai&amp;characterEncoding=utf8"/>
        <property name="username" value="login_root"/>
        <property name="password" value="yy947599"/>
    </dataSource>
</environment>
</environments>
  • transactionManager:事务管理器
    • JDBC:直接使用了JDBC的提交和回滚设置,依赖于从数据源得到的连接来管理事务作用域
    • MANAGED:通过容器去管理事务的整个生命周期
  • dataSource:数据源
    • UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接
    • POOLED:这个数据源是利用池的概念将JDBC连接对象组织起来
    • JNDI:这个数据源的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放在一个JNDI上下文的引用
Mappers标签

加载映射文件

<!--加载映射文件-->
<mappers>
    <!--相对于类路径的资源引用-->
    <mapper resource="mapper/UserMapper.xml" />
    <!--完全限定资源定位符-->
    <mapper url=""/>
    <!--映射器接口实现类的完全限定名-->
    <mapper class="" />
    <!--将包内的映射器接口实现全部注册为映射器-->
    <package name=""/>
</mappers>
Properties标签

将数据源的配置信息单独抽取成一个properties文件

<!--导入properties-->
<properties resource="mysql.properties"/>
TypeAliases标签

别名前

<update id="up" parameterType="domain.User">
        update user set username=#{username},password=#{password} where id=#{id}
</update>

设置别名:在sqlMapConfig.xml

<!--定义别名-->
<typeAliases>
    <typeAlias type="domain.User" alias="user"/>
</typeAliases>

别名后

<update id="up" parameterType="user">
        update user set username=#{username},password=#{password} where id=#{id}
</update>
MyBatis相应的API
  1. SqlSessionFactory build(InputStream inputStream)

    通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象

    openSession():默认开启一个事务,但事务不会自动提交

    openSession(boolean autoCommit):参数为是否自动提交,设置为true,那么不需要手动提交事务

    //获得核心配置文件
    InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml");
    //获得工厂对象
    SqlSessionFactory sql = new SqlSessionFactoryBuilder().build(stream);
    //获得session回话对象
    SqlSession sqlSession = sql.openSession();
  2. SqlSession会话对象

    • <T> T selectOne(String statement, object parameter):查询一个对象
    • <E> List<E> selectList(String statement,Object parameter):查询多个对象
    • int insert(String statement,Object parameter):插入对象
    • int update(String statement,Object parameter):更新对象
    • int delete(String statement,Object parameter):删除对象
    • void commit():提交事务
    • void rollback():回滚事务
Mybatis的Dao层实现
  • 代理开发方式

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

    规范:

    1. Mapper.xml文件中的namespace与mapper接口的全限定名相同
    2. Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
    3. Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
    4. Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
    public interface UserDao {
        List<User> findAll() throws IOException;
        User findById(int id);
    }
    <!--namespace等于接口的全限定名-->
    <mapper namespace="dao.UserDao">
        <select id="findAll" resultType="domain.User">
            select * from user
        </select>
        <!--id等于方法名-->
      <!--parameterType等于方法的参数-->
        <!--resultType等于方法的返回值-->
        <select id="findById" parameterType="int" resultType="domain.User">
            select * from user where id=${id}
        </select>
    </mapper>
    public class ServiceDemo {
        public static void main(String[] args) throws IOException {
            //获取sqlMapConfig.xml配置文件
            InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml");
            //获得工厂对象
            SqlSessionFactory build = new SqlSessionFactoryBuilder().build(stream);
            //获得session回话对象
            SqlSession sqlSession = build.openSession(true);
            UserDao mapper = sqlSession.getMapper(UserDao.class);
            User user = mapper.findById(1);
            System.out.println(user);
        }
    }
MyBatis映射文件深入
  1. 动态sql

    • if

      <select id="findByCondition" parameterType="domain.User" resultType="domain.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>
    • foreach

      <select id="findByIds" parameterType="list" resultType="user">
      select * from user
      <where>
          <!--collection:list集合 array数组-->
          <!--open:以什么开始-->
          <!--close:以什么结束-->
          <!--item:单项名-->
          <!--separator:分隔符-->
          <foreach collection="list" open="id in(" close=")" item="id" separator=",">
              #{id}
          </foreach>
      </where>
      </select>
  2. Sql片段的抽取

    <!--sql语句的抽取-->
    <sql id="selectUser">select * from user</sql>
    <!--使用include引用-->
    <select id="findAll" resultType="domain.User">
    <include refid="selectUser"></include>
    </select>
MyBatis核心配置文件深入
  1. typeHandlers标签

    重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。具体做法:实现org.apache.ibatis.type.TypeHandler接口,或继承一个很便利的类org.apache.ibatis.type.BaseTypeHandler,然后可以选择性的将她映射到一个JDBC类型

    开发步骤:

    • 定义转换类继承类BaseTypeHandler

    • 覆盖4个未实现的方法,其中setNonNullParameter为Java程序设置数据到数据库的回调方法,getNullableResult为查询时mysql字符串类型转换成Java的Type类型的方法

      public class DateTypeHandler extends BaseTypeHandler<Date> {
          //将java类型转换成数据库需要的类型
          @Override
          public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
              long time = date.getTime();
              preparedStatement.setLong(i,time);
          }
          //将数据库中的类型转换为Java类型
          /*
           * Description:〈〉
           * @param resultSet 查询出的结果集 
           * @param s 要转换的字段名称
           * @return: java.util.Date
           */
          @Override
          public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
              //获得结果集中需要的数据转换为Date类型
              long aLong = resultSet.getLong(s);
              Date date = new Date(aLong);
              return date;
          }
          //将数据库中的类型转换为Java类型
          @Override
          public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
              long aLong = resultSet.getLong(i);
              Date date = new Date(aLong);
              return date;
          }
          //将数据库中的类型转换为Java类型
          @Override
          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="handler.DateTypeHandler"/>
      </typeHandlers>
  2. plugins标签

    分页助手PageHelper是将分页的复杂操作进行封装

    开发步骤

    • 导入PageHelper坐标

      <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.2.0</version>
      </dependency>
      <dependency>
        <groupId>com.github.jsqlparser</groupId>
        <artifactId>jsqlparser</artifactId>
        <version>3.2</version>
      </dependency>
    • 在核心配置文件中配置PageHelper

      <!--配置分页助手插件-->
      <plugins>
          <plugin interceptor="com.github.pagehelper.PageInterceptor" />
      </plugins>
    • 测试分页数据获取

      //设置分页的相关参数
      PageHelper.startPage(1,3);
      
      List<User> all = mapper.findAll();
      for (User user : all) {
          System.out.println(user);
      }
      //获得与分页相关参数
      PageInfo<User> pageInfo = new PageInfo<>(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());
MyBatis多表操作

表的关系

  1. 一对一

    用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户

    一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户

    实体订单类

    private int id;
    private Date ordertime;
    private double total;
    //当前订单属于哪一个用户
    private User user;

    OrderMapper.xml配置文件

    <mapper namespace="dao.OrderMapper">
        <resultMap id="orderMap" type="dao.OrderMapper">
            <!--手动指定字段与实体属性的对应关系
                column:数据表的字段名
                property:实体属性名称
            -->
            <id column="oid" property="id" />
            <result column="ordertime" property="ordertime"/>
            <result column="total" property="total"/>
            <!--指定order属性中的user-->
            <!--方法一-->
            <result column="uid" property="user.id"/>
            <result column="rolename" property="user.username"/>
            <result column="roleDesc" property="user.password"/>
            <!--
                方法二
                property:当前实体order中的属性名称private User user
                javaType:当前实体order中的属性的类型
            -->
            <association property="user" javaType="user">
                <id column="uid" property="id"/>
                <result column="rolename" property="username"/>
                <result column="roleDesc" property="password"
            </association>
        </resultMap>
    
        <select id="findAll" resultMap="orderMap">
            SELECT *,o.id oid FROM orders o,sys_role u WHERE o.uid=u.id
        </select>
    </mapper>
  2. 一对多/多对一

    用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户

    一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单

    resultMap中配置集合信息

    <!--
     property:集合名
     ofType:当前集合中的数据类型
    -->
    <collection property="orderList" ofType="com.itheima.domain.Order">
        <!--
         column:数据表中的字段名
         property:实体类中的属性值
     -->
     <result column="oid" property="id"></result>
     <result column="ordertime" property="ordertime"></result>
     <result column="total" property="total"></result>
    </collection>
  3. 多对多

    用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用

    多对多查询的需求:查询用户同时查询出该用户的所有角色

MyBatis注解开发
  1. 常用注解

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

    开发步骤

    • 在方法上添加注解

      public interface UserDao {
          @Insert("insert into user value(#{id},#{username},#{password})")
          void save(User user);
      
          @Update("update user set username=#{username},password=#{password} where id=#{id}")
          void update(User user);
      
          @Delete("delect from user where id=#{id}")
          void del(int id);
      
          @Select("select * from user")
          List<User> select();
      }
    • 在mybatis核心配置文件中添加扫描

      <!--加载映射关系-->
      <mappers>
          <!--指定接口所在的包-->
          <package name="dao"/>
      </mappers>
  2. 一对一的注解开发

    注解 说明
    @Results 代替的是标签\该注解中可以使用单个@Result注解,也可以使用@Result集合。使用格式:@Results({@Result(),@Result()})或@Results(@Result())
    @Result 代替了\标签和\标签
    @Result中属性介绍:
    column:数据库列名
    property:需要装配的属性名
    one:需要使用的@One注解(@Result(one=@One))
    many:需要使用的@Many注解(@Result(many=@Many))
    @One(一对一) 代替了\标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。
    @One注解属性说明:
    select:指定用来多表查询的sqlMapper
    使用格式:@Result(column=" ",property=" ",one=@One(select=" "))
    @Many(多对一) 代替了\标签,是多表查询的关键,在注解中用来指定子查询返回对象集合
    使用格式:@Result(property=" ",column=" ",many=@Many(select=" "))

    编写OrderMapper

    第一种方式:

    public interface OrderMapper {
        @Select("select *,o.id oid from orders o,user u where o.uid=u.id")
        /*
            @Results():代替<resultMap>
            @Result():代替了<id>标签和<result>标签
            column:数据表中的字段名
            property:实体类中的属性
         */
        @Results({
                @Result(column = "oid",property = "id"),
                @Result(column = "ordertime",property = "ordertime"),
                @Result(column = "total",property = "total"),
                @Result(column = "uid",property = "user.id"),
                @Result(column = "username",property = "user.username"),
                @Result(column = "password",property = "user.password")
        })
        List<Order> findAll();
    }

    第二种方式:

    public interface OrderMapper {
        @Select("select * from orders")
        @Results({
                @Result(column = "id",property = "id"),
                @Result(column = "ordertime",property = "ordertime"),
                @Result(column = "total",property = "total"),
                @Result(
                        property = "user",//要封装的属性名称
                        column = "uid",//根据那个字段去查询user表(上面select返回的)
                        javaType = User.class,//返回值封装的实体类
                        //select:代表查询那个接口的方法获得数据(调用别的方法)
                        one = @One(select = "dao.UserDao.findById")
                )
        })
        List<Order> findAll();
    }
  3. 一对多

    与一对一不同的是一对多使用的是@Many

    @Select("select * from user")
    @Results({
            @Result(id = true,column = "id", property = "id"),
            @Result(column = "username",property = "username"),
            @Result(column = "password",property = "password"),
            @Result(
                    property = "roleList",
                    column = "id",
                    javaType = List.class,
                    many = @Many(select = "dao.OrderMapper.findByUid")
            )
    })
    public List<User> findUserAndOrderAll();
Last modification:October 16th, 2020 at 10:43 am
如果觉得我的文章对你有用,请随意赞赏