学习笔记--Mybatis(二)

1.MyBatis的Dao层实现

1.1 传统开发方式(需要写接口实现)

配置完核心配置文件和映射文件之后

 

Dao层创建一个接口类,接口有和数据库之间的交互方法。

然后创建一个实现类,实现类获得配置文件、工厂对象、会话对象之后调用会话方法从数据库获取数据,然后返回给Service层。

Service层拿到数据后实现业务方法。

 

1.2 代理开发方式(不需要写接口实现)

1 代理开发方式介绍

采用Mybatis的代理开发方式实现DAO层的开发,是主流。

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

Mapper接口开发需要遵循以下规范:

1、Mapper.xml文件中的namespace与mapper接口的全限定名相同。

2、Mapper接口方法名和Mapper.xml中定义的每个statement的id相同。

3、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同。

4、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。、

 如图就是规范相同的内容。

 

 

 

 

这样的好处就是只需要编写Mapper接口,不需要写实现类,由Mybatis自动生成动态代理对象帮我们去实现这个接口类

1
2
3
4
5
6
public interface UserMapper {
 
    public List<User> findAll();
 
    public User findById(int id);
}

  

1
2
3
4
5
6
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.findById(1);
System.out.println(user);

  

通过sqlSession.getMapper获取mapper对象。

这个mapper相当于是获取的实现类,直接调用实现类的方法即可。

 

2.MyBatis映射文件深入

2.1 动态sql语句

之前的学的sql都是比较简单的,但有些时候业务逻辑复杂时,我们的SQL是动态变化的,此时我们之前学的就无法满足我们的要求。

 

2.1.1 if 标签

我们会遇到这种情况,在不同场景下,根据不同条件搜索数据库,所以不能用固定的sql语句来搜索数据库,这时候就要用到where+if标签来组成动态语句。

 

按照惯例先写接口类的接口方法

1
public List<User> findByCondition(User user);

  

然后在Mapper.xml中配置sql语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<select id="findByCondition" parameterType="user" resultType="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>

  

<where>标签套在if标签族外,作用与sql语句中where语句相同,当里面if有填充时,起where作用,里面if没填充时,就不起作用。

<if> 标签 当满足if标签里面的条件时,则把其中的语句组成sql语句,如果不满足,则不会组成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  @Test
    public void test1() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
 
        User condition = new User();
        condition.setId(1);
//        condition.setUsername("zhangsan");
        condition.setPassword("123");
        List<User> userList = mapper.findByCondition(condition);
        System.out.println(userList);
    }

  

当把条件中的“zhangsan”注掉时,相当于执行sql语句:

select * from user WHERE id = ? and password = ?

当把条件中的“id”注掉时,相当于执行sql语句:

select * from user WHERE username = ? and password = ?

类似,如果全部注解,则执行:

select * from user

 

2.1.2 foreach标签

foreach标签主要作用是根据集合里的数据循环入sql语句。

 

1
2
3
4
5
6
7
8
<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>

  

collection后面跟的是数据类型,一般是集合或者数组,open是以什么开头,close是以什么结尾,item是每一项的名称,separator是中间间隔。

 

上面的拼接最后 select * from user WHERE id in( ? , ? )

集合中有多少个数字,就会有几个问号。

 

2.2 sql语句的抽取

当我们写sql语句的时候,我们会重复写很多相同的sql语句,比如上述例子中的

1
select * from user

  

我们可以用sql标签来抽取这个重复的sql语句,来达到解耦合的作用。

1
2
3
4
<!-- sql语句的抽取 -->
    <sql id="selectUser">
        select * from user
    </sql>

  

使用sql标签:

1
2
3
4
<!-- 根据id查询-->
    <select id="findById" parameterType="int" resultType="user">
        <include refid="selectUser"/> where id=#{id}
    </select>

  

 

3.MyBatis核心配置文件深入

3.1 typeHandlers标签

将java数据和数据库数据进行转换

 

做法:实现org.apache.ibatis.type.TypeHandler接口 或继承一个很便利的类 org.apache.type.BaseTypeHandler

然后可以选择性地将它映射到一个JDBC类型。

 

开发步骤:

1、定义转换类继承类BaseTypeHandler<T>

1
2
public class DateTypeHandler extends BaseTypeHandler<Date> {
}

2、覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法(java->sql),getNullableResult为查询时mysql的字符串类型转换成java的Type类型的方法(sql->java)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class DateTypeHandler extends BaseTypeHandler<Date> {
 
    //将java类型的数据转换成数据库需要的类型
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
        //date.getTime返回当前时间的毫秒值
        long time = date.getTime();
        //i是参数所在位置,意思是将long形式的数据作为参数填入数据库
        preparedStatement.setLong(i,time);
    }
 
    //将数据库的类型转换成java需要的类型
    // String 参数 要转换的字段名称
    // ResultSet 查询出的结果集
    public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
        //获得结果集中需要的数据(long)转换成Date类型
        long aLong = resultSet.getLong(s);
        Date date = new Date(aLong);
 
        return date;
    }
 
    public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
        //获得结果集中需要的数据(long)转换成Date类型
        long aLong = resultSet.getLong(i);
        Date date = new Date(aLong);
        return date;
    }
 
    public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        long aLong = callableStatement.getLong(i);
        Date date = new Date(aLong);
        return date;
    }
}

  

以后再次碰见Date类型的数据,就会自动使用类型转换器来帮助我们实现数据库和java数据类型之间的转换。

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

1
2
3
<typeHandlers>
      <typeHandler handler="com.xc.handler.DateTypeHandler"/>
  </typeHandlers>

  

4、测试转换是否正确

 

3.2 plugins标签---PageHepler(分页)

MyBatis可以使用第三方插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据。

开发步骤:

1、导入通用PageHepler的坐标

1
2
3
4
5
6
7
8
9
10
11
<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>1.4</version>
</dependency>       

  

2、在mybatis核心配置文件中配置PageHelper插件

1
2
3
4
5
<plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql"/>
        </plugin>
    </plugins>

  

dialect可以理解成“方言”,不同的数据库技术对应不同的值。

3、测试分页数据获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
  public void test5() throws IOException {
      InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
      SqlSession sqlSession = sqlSessionFactory.openSession();
      UserMapper mapper = sqlSession.getMapper(UserMapper.class);
 
      //设置分页相关参数 当前页+每页显示的参数
      PageHelper.startPage(1,3);
 
      List<User> userList = mapper.findAll();
      for (User user : userList) {
          System.out.println(user);
      }
 
      PageInfo<User> pageInfo = new PageInfo<User>(userList);
      System.out.println("当前页:"+ pageInfo.getPageNum());
      System.out.println("每页显示条数:"+ pageInfo.getPageSize());
      System.out.println("总条数:" + pageInfo.getTotal());
      System.out.println("总页数:" + pageInfo.getPages());
      sqlSession.close();
  }

  

pageInfo类也封装了许多关于分页的信息,可以直接调用其中的方法来获取分页的信息。

 

4.MyBatis的多表操作

4.1 一对一查询

本节要完成的是通过订单查询,同时查询到该订单对应的User表的信息

 

sql语句:

1
2
3
<select id="findAll" resultMap="orderMap">
       select *,o.id oid from orders o,user u where o.uid=u.id
   </select>

 

因为两张表都有id字段,所以将orders表中的id起了个别名oid。

查询结果:

 

 

 

如果通过mybatis自动匹配机制,查询到的结果不能直接被封装到订单对象(Order)中的用户对象(User)里,所以要手动配置映射关系。

方法一:

1
2
3
4
5
6
7
8
9
10
11
12
13
<resultMap id="orderMap" type="order">
        <!-- 手动指定字段与实体属性的映射关系
             column:数据表的字段名称
             propery:实体的属性名称
        -->
        <id column="oid" property="id"/>
        <result column="odertime" 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"/>
        <result column="birthday" property="user.birthday"/>
</resultMap>

  

方法二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<resultMap id="orderMap" type="order">
        <!-- 手动指定字段与实体属性的映射关系
             column:数据表的字段名称
             propery:实体的属性名称
        -->
        <id column="oid" property="id"/>
        <result column="odertime" 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"/>-->
<!--        <result column="birthday" property="user.birthday"/>-->
 
        <!-- 这里的property的含义是order类中的user属性
             javaType的含义是该user属性的类型,照理来说应该是com.xc.domain.Order
             因为起了别名,所以内容和前者相同,但含义不同
         -->
        <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>

  

4.2 一对多查询

本节要完成的一对多查询是根据用户,查询该用户所有的订单。

 

对应的sql语句:

1
select *,o.id oid from user u,orders o where u.id=o.uid

  

同样需要手动指定字段和实体属性的映射关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<resultMap id="userMap" type="user">
        <id column="uid" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="birthday" property="birthday"/>
        <!-- 集合信息
            property:集合名称
            ofType:集合中数据的类型
        -->
        <collection property="orderList" ofType="order">
            <id column="oid" property="id"/>
            <result column="ordertime" property="ordertime" jdbcType="DATE"/>
            <result column="total" property="total"/>
        </collection>
    </resultMap>
 
    <select id="findAllOrders" resultMap="userMap">
        select *,o.id oid from user u,orders o where u.id=o.uid
    </select>

  

需要注意 该映射关系,如果遇见sql中的date或datetime属性,需要额外设置其jdbcType的值。

遇到date 设其为“DATE”,

遇到datetime 设其为"TIMESTAMP"

 

id是主键,非常重要,这样的话mybatis会根据你的主键生成对象,一个主键即一个对象。

 

4.3 多对多查询

本节要完成的查询是,多个用户拥有多个角色信息。

 

sql语句:

1
select * from user u,sys_user_role ur,sys_role r where u.id=ur.userId AND ur.roleId=r.id

  

借助中间表来完成

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<resultMap id="userRoleMap" type="user">
        <id column="userId" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="birthday" property="birthday"/>
 
        <collection property="roleList" ofType="role">
            <id column="roleId" property="id"/>
            <result column="roleName" property="roleName"/>
            <result column="roleDesc" property="roleDesc"/>
        </collection>
    </resultMap>
     
    <select id="findUserAndRole" resultMap="userRoleMap">
        select * from user u,sys_user_role ur,sys_role r where u.id=ur.userId AND ur.roleId=r.id
    </select>

  

与一对多类似。

 

5 MyBatis的注解开发

5.1 常用注解

MyBatis也可以使用注解开发方式,减少Mapper映射文件的编写。

@Insert:实现新增

@Update:实现更新

@Delete:实现删除

@Select:实现查询

@Result:实现结果集封装

@Results:可以与@Result一起使用,封装多个结果集

@One:实现一对一结果集封装

@Many:实现一对多结果集封装

 

5.2 简单查询

步骤:

1、写接口映射

2、在接口方法上面利用注解写如sql语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface UserMapper {
 
    @Select("select * from user")
    public List<User> findAll();
 
    @Select("select * from user where id=#{id}")
    public User findById(int id);
 
    @Insert("insert into user values (#{id},#{username},#{password},#{birthday})")
    public void save(User user);
 
    @Update("update user set username=#{username},password=#{password} where id=#{id}")
    public void update(User user);
 
    @Delete("delete from user where id=#{id}")
    public void delete(int id);

 

3、在mybatis核心配置类中设置映射关系

1
2
3
4
5
<!-- 加载映射关系 -->
    <mappers>
        <!-- 指定接口的包-->
        <package name="com.xc.dao"/>
    </mappers>

  

好处:简化了mapper文件的开发,避免对应关系可以极大的加快编写速度和节省精力。

 

5.3 复杂查询

5.3.1 一对一注解查询

方法一:

1
2
3
4
5
6
7
8
9
10
@Select("select *,o.id oid from orders o,user u where o.uid=u.id")
@Results({
        @Result(column = "oid",property = "id"),
        @Result(column = "ordertime",property = "ordertime",jdbcType= JdbcType.DATE),
        @Result(column = "total",property = "total"),
        @Result(column = "uid",property = "user.id"),
        @Result(column = "username",property = "user.username"),
        @Result(column = "password",property = "user.password")
})
public List<Order> findAll();

  

直接查询多表,然后将映射关系写进Results里,result中可以写一一映射关系,值和上述差不多。

方法二:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Select("select * from orders")
    @Results({
            @Result(column = "oid",property = "id"),
            @Result(column = "ordertime",property = "ordertime",jdbcType= JdbcType.DATE),
            @Result(column = "total",property = "total"),
            @Result(
                    property = "user",//封装的属性名称
                    column = "uid",//根据哪个字段去查询user表的数据
                    javaType = User.class,//要封装的实体类型
                    one = @One(select = "com.xc.dao.UserMapper.findById")
            )
    })
    public List<Order> findAll();

  

分2次表查询,首先查订单表,然后根据订单表中用户的id字段,再查用户表,将查到的用户信息封装进对应订单的对象中。

 

5.3.2 一对多查询

一对多同样是分2次表查询

1
2
3
4
5
6
7
8
9
10
11
12
13
@Select("select * from user")
    @Results({
            @Result(id=true,column = "id",property = "id"),
            @Result(column ="username",property = "username"),
            @Result(column = "password",property = "password"),
            @Result(
                    property = "orderList",
                    column = "id",
                    javaType = List.class,
                    many = @Many(select = "com.xc.dao.OrderMapper.findByUid")
            )
    })
    public List<User> findUserAndOrderAll();

  

首先查user表,查到的数据再以user中的id为参数,去查order表,将该id下的所有订单以一对多(@Many)的方式封装进集合中

 

5.3.3 多对多查询

多对多同样是分2次表查询

1
2
3
4
5
6
7
8
9
10
11
12
13
@Select("SELECT * FROM user")
   @Results({
           @Result(id=true,column ="id",property = "id"),
           @Result(column ="username",property = "username"),
           @Result(column ="password",property = "password"),
           @Result(
                   column = "id",
                   property = "roleList",
                   javaType = List.class,
                   many = @Many(select = "com.xc.dao.RoleMapper.findByUid")
           )
   })
   public List<User> findUserAndRoleAll();

  

首先查user表,查到的数据再以user中的id为参数,去查sys_user_role表和sys_role表,先将中间的表的roleid和角色表的id对应,然后再限定userId,将得到的角色全部封装进list中。

 

posted @   小超和你  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
· 面试官:你是如何进行SQL调优的?
点击右上角即可分享
微信分享提示