MyBatis深入学习总结(1.0)

news2025/5/11 4:38:36

MyBatis总结

MyBatis入门操作

简介

原始jdbc操作(查询数据)

img

原始jdbc操作(插入数据)

img

原始jdbc操作的分析

原始jdbbc开发存在的问题如下:

  1. 数据库连接创建、释放频繁造成系统资源的浪费从而影响系统性能
  2. sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变Java代码
  3. 查询操作时,需要手动将结果集中的数据手动封装到实体中。插入操作时,需要手动将实体的数据设置到sql语句的占位符位置

应对上诉问题给出的解决方案:

  1. 使用数据库连接池初始化连接资源
  2. 将sql语句抽取到xml配置文件中
  3. 使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射

什么是MyBatis

  • mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、连接statement等繁杂的过程
  • mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句
  • 最后mybatis框架执行sql并将结果映射为java对象并返回。采用orm思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,这样就不用与jdbc api打交道,就可以完成对数据库的持久化操作。

快速入门

开发步骤:

  1. 添加MyBatis的坐标
  2. 创建user数据库表
  3. 编写user实体类
  4. 编写映射文件UserMapper.xml
  5. 编写测试类

代码实现:

1、添加MyBatis的坐标

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.13</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.12</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.26</version>
    </dependency>

2、创建user数据库表

img

3、编写user实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    // 编码
    private int id;
    // 名字
    private String username;
    // 密码
    private String password;
    // 手机号
    private String phoneNum;
}

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="userMapper">
    <select id="findAll" resultType="com.dc.entity.User">
        select * from sys_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="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/dc/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

6、编写测试类

public class SqlTest {

    @Test
    public void sqlTest() throws IOException {
        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 获取sqlSession工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        // 获取sqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 执行sql语句
        List<User> users = sqlSession.selectList("userMapper.findAll");
        // 打印结果
        System.out.println(users);
        // 释放资源
        sqlSession.close();
    }
}

映射文件概述

img

增删改查

插入数据操作

编写UserMapper映射文件
    <!--插入操作-->
    <insert id="save" parameterType="com.dc.entity.User">
        insert into sys_user(username, email, password, phonenum) value (#{username},#{email},#{password},#{phoneNum})
    </insert>
编写插入实体User的代码
@Test
    public void sqlTest1() throws IOException {

        // 模拟user对象
        User user  = new User("lisi", "25@qq.com", "123", "324234324324");
        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 获取sqlSession工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        // 获取sqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 执行sql语句
        int insert = sqlSession.insert("userMapper.save", user);
        // Mybatis执行提交事务
        sqlSession.commit();
        System.out.println(insert);
        // 释放资源
        sqlSession.close();
    }
插入操作注意问题
  • 插入语句使用insert标签
  • 在映射文件中使用parameterType属性指定要插入的数据类型
  • Sql语句中使用#{实体属性名}方式引用实体中的属性值
  • 插入操作涉及数据库数据变化,所以要使用sqlSession对象显示的提交事务。即sqlSession.commit()

修改数据操作

1、编写UserMapper映射文件
    <!--修改数据-->
    <update id="up" parameterType="com.dc.entity.User">
        update sys_user set username=#{username}, password=#{password} where id = #{id}
    </update>
2、编写修改实体User的代码
    @Test
    public void sqlTest2() throws IOException {

        // 模拟user对象
        User user = new User();
        user.setId(3);
        user.setUsername("wang");
        user.setPassword("ersrsr");
        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 获取sqlSession工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        // 获取sqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 执行sql语句
        sqlSession.update("userMapper.up", user);
        // Mybatis执行提交事务
        sqlSession.commit();
        // 释放资源
        sqlSession.close();
    }
修改操作注意问题
  • 修改数据使用update标签
  • 修改操作使用的API是sqlSession.update(“命名空间.id”,实体对象)

删除数据操作

编写UserMapper映射文件
    <!--删除数据-->
    <delete id="delete" parameterType="com.dc.entity.User">
        delete from sys_user where id = #{id}
    </delete>
编写删除数据的代码
    @Test
    public void sqlTest3() throws IOException {

        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 获取sqlSession工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        // 获取sqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 执行sql语句
        sqlSession.update("userMapper.delete", 11);
        // Mybatis执行提交事务
        sqlSession.commit();
        // 释放资源
        sqlSession.close();
    }
删除操作注意问题
  • 删除语句使用delete标签
  • Sql语句中使用#{任意字符串}方式引用传递的单个参数
  • 删除操作使用的API是sqlSession.delete(“命名空间.id”,Object)

核心配置文件

MyBatis核心配置文件层级关系

img

MyBatis常用配置解析

environments标签

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

img

其中,事务管理器(transactionManager)类型有两种:

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

其中,数据源(dataSource)类型有三种:

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

该标签的作用是加载映射的,加载方式有如下几种:

  • 使用相对于类路径的资源引用,例如:<mapper resource=“org/mybatis/bulider/AuthorMapper.xml”/>
  • 使用完全限定资源定位符(URL),例如:<mapper url=“file:///var/mappers/AuthorMapper.xml”/>
  • 使用映射器接口实现类的完全限定类名,例如:<mapper class=“org.mybatis.builder.AuthorMapper”/>
  • 将包内的映射器接口实现全部注册为映射器,例如:<package name=“org.mybatis.builder”/>

常用配置解析

properties标签

实际开发中,习惯将数据的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件

img

typeAliases标签

类型别名是为java类型设置一个短的名字,原来的类型名称配置如下:

img

mybatis框架已经设置好的一些常用的类型的别名

别名数据类型
stringString
longLong
intInteger
doubleDouble
booleanBoolean
…………
总结

常用配置

img

相应API

SQL Session工厂构建器SqlSessionFactoryBulider

常用API:SqlSessionFactory bulid(InputStream inputSream)

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

String resource = "org/mybatis/bulider/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBulider builder = new SqlSessionFactoryBulider();
SqlSessionFactory factory = builder.build(inputStream);

其中,Resources工具类,这个类在org.apache.ibatis.io包中。Resources类帮助你从类路径下、文件系统或一个web URL中加载资源文件

SqlSession工厂对象SqlSessionFactory

SqlSessionFactory有多个方法创建SqlSession实例。常用的有如下两个:

方法解释
openSession()会默认开启一个事务,但事务不会自动提交,也就意味着需要手动提交该事务,更新操作数据才会持久化到数据库
openSession(boolean autoCommit)参数为是否自动提交,如果设置为true,那么不需要手动提交事务

SqlSession会话对象

SqlSession实例在MyBatis中是非常强大的一个类。在这里你会看到所有执行语句、提交或回滚事务和获取映射器实例的方法。执行语句的方法主要有:

<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层实现方式

传统开发方式

编写UserDao接口

public interface UserDao{
    List<User> findAll() throws IOException;
}

编写UserDaoImpl实现

public class UserDaoImpl implements UserDao{
    public List<User> findAll() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactory.openSession();
        List<User> userList = sqlSession.selectList("userMapper.findAll");
        sqlSession.close();
        return userList;
    }
}

测试传统方式

@Test
public void testTradItionDao() throws IOException {
    UserDao userDao = new UserDaoImpl();
    List<User> all = userDao.findAll();
    System.out.println(all);
}

代理开发方式

代理开发方式介绍

采用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的类型相同

img

测试代理方式

public class UserService {

    public static void main(String[] args) 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 id = mapper.findById(1);
        System.out.println(id);

    }
}

MyBatis映射文件深入

动态sql语句

概述

有时候业务逻辑复杂时,在MyBatis的映射文件中,sql是动态变化的,此时之前的sql就不能满足需求了

<if>

根据实体类的不同取值,使用不同的SQL语句来进行查询。比如在id如果不为空时可以根据id查询,如果username不为空时还要加入用户名作为条件。这种情况在多条件组合查询中经常会遇到

    <!--根据username进行操作-->
    <select id="findCondition" parameterType="com.dc.entity.User" resultType="com.dc.entity.User">
        select * from sys_user
        <where>
            <if test="id != null">
                id = #{id}
            </if>
            <if test="username != null">
                and username = #{username}
            </if>
        </where>
    </select>

当查询条件id和username都存在时,控制台打印的sql语句如下:

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setUsername("lucy");
user.setId(1);
User condition = mapper.findCondition(user);
System.out.println(condition);

img

当查询条件只有id存在时,控制台打印的sql语句如下:

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(1);
User condition = mapper.findCondition(user);
System.out.println(condition);

img

<foreach>

循环执行sql的拼接操作,例如:select * from user where id in(1,2,3).

    <select id="findByIds" parameterType="list" resultType="com.dc.entity.User">
        select * from sys_user
        <where>
            <foreach collection="array" open="id in (" item="id" close=")" separator=",">
                #{id}
            </foreach>
        </where>
    </select>

测试代码片段如下:

    @Test
    public void sqlTest5() throws IOException {

        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 获取sqlSession工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        // 获取sqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 执行sql语句
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int[] ids = {1, 2, 4};

        List<User> byIds = mapper.findByIds(ids);
        System.out.println(byIds);
    }

img

foreach标签的属性含义如下

<foreach>标签用于遍历集合,它的属性:

  • collection:代表要遍历的集合元素,注意编写时不要写#{}
  • open:代表语句的开始部分
  • close:代表结束部分
  • item:代表遍历集合的每个元素,生成的变量名
  • sperator:代表分隔符

SQL片段抽取

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

img

核心配置文件深入

typeHandlers标签

无论是MyBatis在预处理语句(Preparedstatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成java类型,下表描述了一些默认的类型处理器:

img

你可以重写类型处理器或创建自己的类型处理器来处理不支持的或非标准的类型。具体做法为:实现org.apache.ibatis.type.TypeHandler接口。或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler,然后可以选择性的将它映射到一个jdbc类型。如:一个java中的Date数据类型,想将之存到数据库的时候存成一个1970年至今的毫秒值,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换

开发步骤:

  1. 定义转换类继承类BaseTypeHandler<T>
  2. 覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时mysql的字符串类型转换成java的Type类型的方法
  3. 在MyBatis核心配置文件中进行注册
  4. 测试转换是否正确

代码展示:

public class DateTypeHandler extends BaseTypeHandler<Date> {

    // 将Java类型转换为数据库需要的类型
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Date date, JdbcType jdbcType) throws SQLException {
        long time = date.getTime();
        ps.setLong(i, time);
    }

    // 将数据库类型转换为java类型
    // String参数 要转换的数据名称
    // ResultSet:查询出的结果集
    @Override
    public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
        // 获得结果集中需要的数据(long)转换成Date类型返回
        Long along = rs.getLong(columnName);
        Date date = new Date(along);
        return date;
    }

    // 将数据库类型转换为java类型
    @Override
    public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        Long along = rs.getLong(columnIndex);
        Date date = new Date(along);
        return date;
    }

    // 将数据库类型转换为java类型
    @Override
    public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        Long along = cs.getLong(columnIndex);
        Date date = new Date(along);
        return date;
    }
}

注册:

    <!--注册类型处理器-->
    <typeHandlers>
        <typeHandler handler="com.dc.handler.DateTypeHandler"></typeHandler>
    </typeHandlers>

测试

    @Test
    public void sqlTest6() throws IOException {

        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 获取sqlSession工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        // 获取sqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 执行sql语句
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setUsername("cashi");
        user.setPassword("sbf");
        user.setBirthday(new Date(System.currentTimeMillis()));
        mapper.save(user);
        sqlSession.commit();
        sqlSession.close();
    }

img

plugins标签

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

开发步骤:

  1. 导入通用pageHelper的坐标
  2. 在mybatis核心配置文件中配置pageHelper插件
  3. 测试分页数据获取

代码展示:

1、导入通用PageHelper坐标

    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>5.3.0</version>
    </dependency>
    <dependency>
      <groupId>com.github.jsqlparser</groupId>
      <artifactId>jsqlparser</artifactId>
      <version>4.2</version>
    </dependency>

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

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
        </plugin>
    </plugins>

3、测试分页代码实现

    @Test
    public void sqlTest8() throws IOException {

        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 获取sqlSession工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        // 获取sqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 执行sql语句
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 设置分页参数

        List<User> userList = mapper.findAll();
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

获得分页相关的其他参数

        // 获得与分页相关参数
        PageInfo<User> pageInfo = new PageInfo<>(userList);
        System.out.println("总条数" + pageInfo.getTotal());
        System.out.println("总页数" + pageInfo.getPages());
        System.out.println("当前页" + pageInfo.getPageNum());
        System.out.println("每页显示长度" + pageInfo.getPageSize());
        System.out.println("是否第一页" + pageInfo.isIsFirstPage());
        System.out.println("是否是最后一页" + pageInfo.isIsLastPage());

多表查询

一对一查询

一对一查询的模型

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

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

img

对应的sql语句:select * from order o, user u where u.id = o.uid;

查询结果如下:

img

创建Order和User实体

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {

    private int id;
    private Date ordertime;
    private double total;
    private User user;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private int id;
    private String username;
    private String password;
    private String birthday;
}

创建OrderMapper接口

public interface OrderMapper {

    List<Order> findAll();
}

配置OrderMapper.xml

<mapper namespace="com.dc.mapper.OrderMapper">
    <!--方式一-->
<!--<resultMap id="orderMap" type="com.dc.entity.Oder">
    <result column="id" property="id"/>
    <result column="ordertime" property="ordertime"/>
    <result column="total" property="total"/>
    <association property="user" javaType="com.dc.entity.User">
        <result column="uid" property="id"/>
        <result column="username" property="username"/>
        <result column="pssword" property="password"/>
        <result column="birthday" property="birthday"/>
    </association>
</resultMap>-->
    <!--方式二-->
    <resultMap id="orderMap" type="com.dc.entity.Order">
        <result column="uid" property="user.id"></result>
        <result column="username" property="user.username"></result>
        <result column="password" property="user.password"></result>
        <result column="birthday" property="user.birthday"></result>
    </resultMap>
    <select id="findAll" resultMap="orderMap">
        select * from user u, `order` r where r.uid = u.id
    </select>

测试

public class SqlTest {

    @Test
    public void SqlTest() throws IOException {
        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 获取sqlSession工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        // 获取sqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 执行sql语句
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        List<Order> all = mapper.findAll();

        for (Order oder : all) {
            // 打印结果
            System.out.println(oder);
        }

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

img

一对多查询

模型

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

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

img

一对多查询的语句

对应的sql语句为:

select *,o.id oid from user u left join `order` o on u.id=o.uid;

img

修改user实体

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private int id;
    private String username;
    private String password;
    private String birthday;

    // 代表当前用户具备哪些订单
    private List<Order> orderList;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {

    private int id;
    private Date ordertime;
    private double total;
    private User user;
}

创建UserMapper接口

public interface OrderMapper {

    List<User> find();
}

配置UserMapper.xml

<resultMap id="userMap" type="com.dc.entity.User">
    <result column="id" property="id"/>
    <result column="username" property="username"/>
    <result column="password" property="password"/>
    <result column="birthday" property="birthday"/>
    <collection property="orderList" ofType="com.dc.entity.Order">
        <result column="oid" property="id"/>
        <result column="ordertime" property="ordertime"/>
        <result column="total" property="total"/>
    </collection>
</resultMap>

<select id="find" resultMap="userMap">
     select *,o.id oid from user u left join `order` o on u.id=o.uid;
 </select>

测试结果

 @Test
    public void SqlTest() throws IOException {
        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        // 获取sqlSession工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        // 获取sqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 执行sql语句
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        List<User> all = mapper.find();
        for (User oder : all) {
            // 打印结果
            System.out.println(oder);
        }

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

img

多表查询

模型

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

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

img

对应的sql语句:

select u.*,r.*,r.id,u.id from sys_user u left join sys_user_role ur on u.id=ur.userId inner join sys_role r on ur.roleId=r.id;

img

创建role实体,修改User实体

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
    private int id;
    private String roleName;
    private String roleDesc;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String username;
    private String email;
    private String password;
    private String phoneNum;

    // 代表当前用户具备哪些角色
    private List<Role> orderList;
}

添加UserMapper接口方法

public interface UserMapper {

    public List<User> findAllUserAndRole();
}

配置UserMapper.xml

<mapper namespace="com.dc.mapper.UserMapper">
    <resultMap id="userRoleMap" type="com.dc.model.User">
        <result column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="email" property="email"/>
        <result column="phoneNum" property="phoneNum"/>
        <collection property="orderList" ofType="com.dc.model.Role">
            <result column="rid" property="id"/>
            <result column="rolename" property="roleName"/>
            <result column="roleDesc" property="roleDesc"/>
        </collection>
    </resultMap>

    <select id="findAllUserAndRole" resultMap="userRoleMap">
        select u.*,r.*,r.id,u.id from sys_user u left join sys_user_role ur on u.id=ur.userId inner join sys_role r on ur.roleId=r.id;

    </select>

测试

@Test
    public void sqlTest() throws IOException {
        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlConfig.xml");
        // 获取sqlSession工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        // 获取sqlsession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 执行sql语句
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allUserAndRole = mapper.findAllUserAndRole();
        for (User user : allUserAndRole) {
            System.out.println(user.getUsername());
            List<Role> roleList = user.getOrderList();
            for (Role o : roleList) {
                System.out.println(o);
            }
        }
    }

img

注解开发

常用注解

基本的CRUD操作

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

增删改查

public class SqlTest {

    private UserMapper userMapper;

    @Before
    public void before() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        userMapper = sqlSession.getMapper(UserMapper.class);
    }

    @Test
    public void testSave() {
        User user = new User();
        user.setUsername("zghang");
        List<User> allUserAndRole = userMapper.findAllUserAndRole();
        System.out.println(allUserAndRole);
    }
}
@Test
public void testAdd() {
	User user = new User();
	user.setUsername("测试数据");
	user.setPassword("123");
	user.setBirthday(new Date());
	userMapper.add(user);
}

@Test
public void testUpdate() throws IOException {
	User user = new User();
	user.setId(16);
	user.setUsername("测试数据修改");
	user.setPassword("abc");
	user.setBirthday(new Date());
	userMapper.update(user);
}

@Test
public void testDelete() throws IOException {
	userMapper.delete(16);
}

@Test
public void testFindById() throws IOException {
	User user = userMapper.findById(1);
	System.out.println(user);
}

@Test
public void testFindAll() throws IOException {
	List<User> all = userMapper.findAll();
	for(User user : all){
		System.out.println(user);
	}
}

修改MyBaits的核心配置文件,使用了注解替代的映射文件,所以只需要加载使用了注解的mapper接口即可

<mappers>
	<!--扫描使用注解的类-->
	<mapper class="com.dc.mapper.UserMapper"></mapper>
</mappers>

或者指定扫描包含映射关系的接口所在的包

<mappers>
	<!--扫描使用注解的类所在的包-->
	<package name="com.dc.mapper"></package>
</mappers>

MyBatis的注解实现复杂映射开发

实现复杂映射之前可以在映射文件中通过配置<resultMap>来实现,使用注解开发之后,可以使用@Results注解、@Result注解、@One注解、@Many注解组合完成复杂关系的配置

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

一对一查询的语句

查询sql语句

select * from orders;
select * from user where id=查询出订单的uid;

创建Order和User实体

public class Order {
	private int id;
	private Date ordertime;
	private double total;
	//代表当前订单从属于哪一个客户
	private User user;
}

public class User {
	private int id;
	private String username;
	private String password;
	private Date birthday;
}

使用注解配置Mapper

public interface OrderMapper {
@Select("select * from orders")
@Results({
	@Result(id=true,property = "id",column = "id"),
	@Result(property = "ordertime",column = "ordertime"),
	@Result(property = "total",column = "total"),
	@Result(property = "user",column = "uid",
		javaType = User.class,
		one = @One(select ="com.dc.mapper.UserMapper.findById"))
	})
	List<Order> findAll();
}

public interface UserMapper {
	@Select("select * from user where id=#{id}")
	User findById(int id);
}

测试结果

private UserMapper userMapper;

    @Before
    public void test1() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        userMapper = sqlSession.getMapper(UserMapper.class);
    }

@Test
public void testSelectOrderAndUser() {
	List<Order> all = orderMapper.findAll();
	for(Order order : all){
	System.out.println(order);
	}
}

多对多查询的模型

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

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

查询的sql语句:

select * from user;
select * from orders where uid=查询出用户的id;

修改User实体

public class Order {
	private int id;
	private Date ordertime;
	private double total;
	//代表当前订单从属于哪一个客户
	private User user;
}

public class User {
	private int id;
	private String username;
	private String password;
	private Date birthday;
	//代表当前用户具备哪些订单
	private List<Order> orderList;
}

使用注解配置Mapper

public interface UserMapper {
	@Select("select * from user")
	@Results({
	@Result(id = true,property = "id",column = "id"),
	@Result(property = "username",column = "username"),
	@Result(property = "password",column = "password"),
	@Result(property = "birthday",column = "birthday"),
	@Result(property = "orderList",column = "id",
		javaType = List.class,
		many = @Many(select = "com.dc.mapper.OrderMapper.findByUid"))
	})
	List<User> findAllUserAndOrder();
}

public interface OrderMapper {
	@Select("select * from orders where uid=#{uid}")
	List<Order> findByUid(int uid);
}

测试结果

List<User> all = userMapper.findAllUserAndOrder();
for(User user : all){
	System.out.println(user.getUsername());
	List<Order> orderList = user.getOrderList();
	for(Order order : orderList){
		System.out.println(order);
	}
	System.out.println("-----------------------------");
}

多对多查询

查询的sql语句

select * from user;
select * from role r,user_role ur where r.id=ur.role_id and ur.user_id=用户的id

创建Role实体,修改User实体

public class User {
	private int id;
	private String username;
	private String password;
	private Date birthday;
	//代表当前用户具备哪些订单
	private List<Order> orderList;
	//代表当前用户具备哪些角色
	private List<Role> roleList;
}

public class Role {
	private int id;
	private String rolename;
}

使用注解配置Mapper

public interface UserMapper {
	@Select("select * from user")
	@Results({
		@Result(id = true,property = "id",column = "id"),
		@Result(property = "username",column = "username"),
		@Result(property = "password",column = "password"),
		@Result(property = "birthday",column = "birthday"),
		@Result(property = "roleList",column = "id",
			javaType = List.class,
			many = @Many(select = "com.itheima.mapper.RoleMapper.findByUid"))
		})
	List<User> findAllUserAndRole();}
}

public interface RoleMapper {
	@Select("select * from role 
		r,user_role ur where 
		r.id=ur.role_id and 
		ur.user_id=#{uid}")
	List<Role> findByUid(int uid);
}

测试结果

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.findAllUserAndRole();
for(User user : all){
	System.out.println(user.getUsername());
	List<Role> roleList = user.getRoleList();
	for(Role role : roleList){
		System.out.println(role);
	}
	System.out.println("----------------------------------");
}

img

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/601630.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3年经验,面试测试开发岗25K都拿不到了吗?这么坑?

最近后台读者说自己最近在疯狂投简历&#xff0c;有的石沉大海&#xff0c;但还好不是全军覆没。前两天好不容易熬到了阿里的四面&#xff0c;跟我聊了一下&#xff0c;面试官拿哪些题为难了他&#xff1f; 前面几题还好&#xff0c;问的是有关JVM的一些问题&#xff0c;比如说…

一道Java经典面试题 99%都有可能做错

前言 最近在面试中遇到一个关于位运算的题目 如下图 请问这个aString打印值是多少 学过位运算我们都知道 9<<4位 用2进制表示就是0000 1001 如果按照我之前的算法就是 0000 10001 向左位运算4 得到 1001 0000 这个时候我们得到的值就是 12816 144 拿到144这个值我们再…

session.upload_progress文件包含漏洞

session.upload_progress文件包含漏洞 前言 之前学习了该漏洞&#xff0c;但是没有做笔记&#xff0c;导致容易遗忘。在此用一个题目来理解session.upload_progress漏洞 基础知识 session存储 我们在phpinfo可以看到session的存储路径&#xff1a; 以下是一些session在lin…

【Python文本处理】基于运动路线记录GPX的文件解析,GPX转SRT字幕文件(不需要安装三方库)

【Python文本处理】基于运动路线记录GPX的文件解析&#xff0c;GPX转SRT字幕文件&#xff08;不需要安装三方库&#xff09; 解析和转换 GPX文件格式 GPX文件本身其实就是坐标、海拔、时间、心率等综合性的xml文件 如图&#xff1a; 海拔&#xff1a;ele 时间&#xff1a;t…

【GTest】使用CMakeLitsts.txt构建Windows和Linux的跨平台GoogleTest项目(非常详细+亲测有效)

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化 &#x1f449;专__注&#x1f448;&#xff1a;专注主流机器人、人工智能等相关领域的开发、…

00): Can‘t connect to MySQL server on ‘localhost:3306‘ (10061)

好久没有使用数据库&#xff0c; 连接数据库报上面的错误&#xff0c;尝试了网上的方法还是没有成功&#xff0c;思索之后想起之前手动关闭了mysql的服务&#xff0c;Windows启动时mysql服务不会自动启动&#xff0c;成功启动mysql服务后再次连接数据库&#xff0c;正常连接。 …

keil 使用问题总结

1. 编译报错 1.1 …\USER\stm32f10x.h(428): error: #67: expected a “}” ADC1_2_IRQn 18, /*!< ADC1 and ADC2 global Interrupt */*** Using Compiler V5.06 update 4 (build 422), folder: D:\keil_v537\install\ARM\ARM…

滴滴和华为5年,分享一下真实的划水经验....

先简单交代一下背景吧&#xff0c;某不知名 985 的本硕&#xff0c;17 年毕业加入华为&#xff0c;之后跳槽到了滴滴&#xff0c;一直从事软件测试的工作。之前没有实习经历&#xff0c;算是5年的工作经验吧。 这5年之间完成了一次晋升&#xff0c;换了一家公司&#xff0c;有…

朋友圈九宫格照片怎么做?一键图片分割

相信朋友们都看过这样发朋友圈的方式&#xff0c;一张图片发出九宫格的效果&#xff1a; 或者是在各大社交平台、引流平台&#xff0c;这种发图的方法已经屡见不鲜了&#xff0c;可当自己上网搜【图片分割】、【宫格切图】等等&#xff0c;要么就是要注册账号登录才能操作&…

大数据---聚类分析概述及聚类评估

聚类概述: 什么是聚类&#xff1f; 是把数据对象集合按照相似性划分成多个子集的过程。每个子集是一个簇&#xff08;cluster&#xff09;&#xff0c;分类的最终效果&#xff1a;使得簇中的对象彼此相似&#xff0c;但与其他簇中的对象相异。聚类是无监督学习&#xff0c;因…

ses价签墨水屏折腾-01

前言 一直想玩玩墨水屏&#xff0c;这次咸鱼找了全新的ses价签&#xff0c;而且价格还很便宜&#xff0c;于是买了一个2.66寸三色&#xff0c;和一个4.2寸三色的墨水屏幕&#xff0c;经过几天的折腾终于能正常显示了。 显示效果如下&#xff1a; 折腾来折腾去&#xff0c;发…

DMES-2023第十三届数字营销与电商峰会大会 火热报名中

2023第十三届数字营销与电商峰会将于7月6-7日在上海召开&#xff01; 本次峰会线上线下同步举行&#xff0c;将通过七大热点主题帮助品牌主突破模式能力&#xff0c;回归初心、精耕细作&#xff0c;实现可持续增长&#xff1b;提升产品能力&#xff0c;实现可持续长期增长&…

智能名片如何缓解社交商务时的尴尬场景?

2023年微信月活用户已超过13.1亿&#xff0c;而小程序也成为了一个神奇的窗口。透过这个窗口&#xff0c;越来越多的人发现了微信生态内的巨大商机&#xff0c;通过小程序也涌现出许多新颖的玩法。 智能名片小程序&#xff0c;基于微信与 AI 技术开发&#xff0c;将企业名片系…

强化学习基础篇【1】:基础知识点、马尔科夫决策过程、蒙特卡洛策略梯度定理、REINFORCE 算法

【强化学习原理+项目专栏】必看系列:单智能体、多智能体算法原理+项目实战、相关技巧(调参、画图等、趣味项目实现、学术应用项目实现 专栏详细介绍:【强化学习原理+项目专栏】必看系列:单智能体、多智能体算法原理+项目实战、相关技巧(调参、画图等、趣味项目实现、学术应…

MKS SERVO4257D 闭环步进电机_系列6 串口(RS485)通讯示例

第1部分 产品介绍 MKS SERVO 28D/35D/42D/57D 系列闭环步进电机是创客基地为满足市场需求而自主研发的一款产品。具备脉冲接口和RS485/CAN串行接口&#xff0c;支持MODBUS-RTU通讯协议&#xff0c;内置高效FOC矢量算法&#xff0c;采用高精度编码器&#xff0c;通过位置反馈&a…

CAR-T药物|疗法适应症|市场销售-上市药品前景分析

对患有癌症的人来说&#xff0c;能够幸运地度过5年大关是一种成功&#xff0c;而能够成功地度过10年大关则是一种奇迹。Emily作为全球第一个接受CAR-T治疗成功的白血病儿童患者&#xff0c;至今已成功摆脱癌症11年之久。 ①CAR-T细胞治疗&#xff08;Emily Whitehead治疗案例时…

Benewake(北醒) 中距 TF40 40m介绍以及资料整理

目录 1 前言2 产品介绍3 产品快速测试3.1 产品规格书及使用说明书3.2 通用上位机测试说明3.3 通用指令串口助手使用说明3.4 产品快速测试说明 4 基于开源硬件的运用整理4.1 在开源Arduino上的运用 1 前言 本文包含【Benewake(北醒) 】中距 TF40 40m介绍以及资料整理 详细请参考…

Arduino软件+ ESP8266 MCU开发项目之一键式配网操作(有掉电保护),可继续开发成为--WIFI放大器项目。

下载本次ESP8266 MCU开发项目之一键式配网 代码文件 (11条消息) 配网ESP8266MUC.rar资源-CSDN文库 流程图讲解 视频展示效果 配网ESP8266 项目可待 改进/升级 的地方 1&#xff1a;配网流程UI界面单调&#xff0c;可搭配复杂UI配网界面 2&#xff1a;配网流程可看作是WIFI放…

NSS周常刷密码(2)

[GWCTF 2019]babyRSA 解答过程在脚本内 from Crypto.Util.number import * import gmpy2 import sympy import z3e 0x10001 N63658514959457474690903016018269086622290925646484729178300065183722792133723789965128794359777327094438403485892529574488072710160684141…

移动云镜像配置BC-Linux --配置centos 6-7-8版本参考

移动云BC linux 也是一个非常不错的镜像站. 可以加速国内下载. 非常好用, 速度比阿里 等那些流行的镜像站快一些. 下面介绍详解操作配置, 供参考 移动云开源镜像站-Linux系统订阅服务 移动云开源镜像站&#xff0c;包含BC-Linux各版本的软件仓库&#xff0c;对外提供软件升级…