MyBatis深入学习总结

news2024/11/16 7:47:31

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/612243.html

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

相关文章

深度学习基础知识-tf.keras实例:衣物图像多分类分类器

参考书籍&#xff1a;《Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow, 2nd Edition (Aurelien Geron [Gron, Aurlien])》 本次使用的数据集是tf.keras.datasets.fashion_mnist&#xff0c;里面包含6w张图&#xff0c;涵盖10个分类。 import tensorflo…

Linux中的ACL以及加固

ACL访问控制 // ACL:Access Control List 访问控制列表 // -p :以原始格式显示 ACL [rootzbx ~]# getfacl -p /root/ // 查看ACL策略 # file: /root/ # owner: root # group: root user::r-x group::r-x other::--- 设置ACL策略 // -m : 修改文件的ACL // -b : 表示删除所有的…

RedisGraph的图存储模型

1 overview 在RedisGraph的整体架构中&#xff0c;非常简略的概括了RedisGraph的图存储模型&#xff1a; RedisGraph使用DataBlock来存储node和edge的属性。RedisGraph使用稀疏矩阵来表示图&#xff0c;稀疏矩阵的存储格式为按行压缩的稀疏矩阵&#xff08;Compressed Sparse…

同浏览器下多窗口进行跨源通信、同源通信

同浏览器下多窗口进行跨源通信、同源通信 多页面通信运用到了“发布订阅”的设计模式&#xff0c;一个页面发布指令&#xff0c;其他页面进行订阅并进行相应的行为操作&#xff01; 一、跨源通信 window.postMessage() window.postMessage() 方法可以安全地实现跨源通信。通常…

Qt6之调用Windows下vc生成的动态链接库dll

Qt是跨平台工具&#xff0c;显然能和windows的动态库一起使用。 在Windows操作系统上&#xff0c;库以文件的形式存在&#xff0c;并且可以分为动态链接库(DLL) 和静态链接库两种。动态链接库文控以.dll为后缀名&#xff0c;静态链接库文控以.lib为后缀名。不管是动态链接库还是…

独立站卖家如何应对PayPal风险?3大策略教你安全收款!

PayPal是全球风险控制做得最好的第三方在线支付平台&#xff0c;PayPal付款是钱直接到卖家PayPal账户。但随着外贸交易的日益发展&#xff0c;恶意买家的问题也越来越多。如何防范风险&#xff0c;保证收款安全&#xff0c;成为独立站卖家们所关注的问题。下面为大家分享三种策…

背包DP-入门篇

目录 01背包&#xff1a; 完全背包&#xff1a; 多重背包&#xff1a; 分组背包&#xff1a; 01背包&#xff1a; [NOIP2005 普及组] 采药 - 洛谷https://www.luogu.com.cn/problem/P1048 01背包背景 在一个小山上&#xff0c;有个n个黄金和一个容量为w的背包&#xff0c;…

【Python】深度理解Class类、Object类、Type元类的概念和关系

深度理解Class类、Object类、Type元类 1.Class类、Object类、Type元类的表面关系2.Class、Object、Type解释3.关系详解4.那么如何看待object、type在Python面对对象概念中的一席之地呢&#xff1f;5.那么object、type扮演了什么样的角色呢&#xff1f;他们对class又分别做了什么…

【计算机组成】Cache与CPU的直接映射、全相联映射与组相联映射

一.Cache与CPU需要映射的原因 CPU准备访问内存时&#xff0c;会先问问cache存储器有没有已经提前准备好了数据&#xff0c;如果没有则再找内存要&#xff1a; 如果Cache刚好命中&#xff0c;则直接从Cache中读取数据&#xff1a; 如果Cache没有命中&#xff08;Cache失效&#…

时序数据库InfluxDB快速入门使用

推荐博客&#xff1a; Influxdb中文文档 linux安装influxdb Influxdb安装、启动influxdb控制台、常用命令、Influx命令使用、Influx-sql使用举例、Influxdb的数据格式、Influxdb客户端工具 1.安装 1、influxdb数据库官网的下载链接&#xff1a; https://portal.influxdata.c…

如何利用MES系统进行生产防呆防错?

一、认识MES系统的防呆防错功能 首先&#xff0c;我们要清楚了解&#xff0c;什么是MES系统的防呆防错。MES系统防呆防错是指利用MES系统来避免生产过程中的错误和缺陷&#xff0c;保障生产排程和生产过程顺利进行的过程。MES系统防呆防错包括以下方面&#xff1a; 1. 自动识别…

relation-graph关系图谱组件2.0版本遇到的问题

前提&#xff1a;之前已经写过一篇1.1版本的问题&#xff0c;这里就不过多讲了&#xff08;如果想要解决火狐低版本兼容&#xff0c;看那个就行&#xff09; 这次主要讲的是和1.X版本的区别和一些其它问题 区别 参数名不同&#xff1a;以前的links>lines (虽然现在links也…

遇见未来,降低职场焦虑——中国人民大学与加拿大女王大学金融硕士来助力

身在职场的你有感到一丝丝的焦虑吗&#xff1f;偶尔的小焦虑可以作为我们工作中的动力&#xff0c;时刻提醒我们保持奋进。预见未来才能遇见未来&#xff0c;随着社会经济不断发展&#xff0c;没有什么是一成不变的。处于职场上升期的我们更要懂得未雨绸缪&#xff0c;增加自身…

ClickHouse集群搭建总结

简介 ClickHouse是俄罗斯最大的搜素引擎Yandex于2016年开源的列式数据库管理系统&#xff0c;使用C 语言编写, 主要应用于OLAP场景。 使用理由 在大数据量的情况下&#xff0c;能以很低的延迟返回查询结果。 笔者注&#xff1a; 在单机亿级数据量的场景下可以达到毫秒级的查询…

SpringCloudAlibaba 微服务生态

一 微服务架构 1.1 微服务 微服务其实是一种架构风格&#xff0c;我们在开发一个应用的时候这个应用应该是由一组小型服务组成&#xff0c;每个小型服务都运行在自己的进程内&#xff1b;小服务之间通过HTTP的方式进行互联互通。 1.2 微服务架构的常见问题 一旦采用微服务系…

ChatGPT 之后,B 端产品设计会迎来颠覆式革命吗?| Liga妙谈

近日&#xff0c;脑机接口公司 Neuralink 宣布&#xff0c;其植入式脑机接口设备首次人体临床研究已被准许启动。遥想当年&#xff0c;我们还嘲讽罗老师「动嘴做 PPT」&#xff0c;谁曾想不久后我们可能连嘴都不用动&#x1f64a;。 脑机接口何时会引爆人机交互革命尚未可知&a…

简述三观;

文章目录 三观世界观人生观价值观三观不合怎么看三观不正: 教养育儿教育心智不成熟的表现 三观 指人生观&#xff0c;世界观和价值观; https://wenku.baidu.com/view/102a655fd4bbfd0a79563c1ec5da50e2534dd1d8.html?fraladdin664466&ind1&_wkts_1685949448098&…

深入理解API网关Kong:动态负载均衡配置

深入理解API网关Kong&#xff1a;动态负载均衡配置 背景 在 NGINX 中&#xff0c;负载均衡的配置主要在 upstream 指令中进行。upstream 指令用于定义一个服务器群组和负载均衡方法。客户端请求在这个服务器群组中进行分发。 NGINX 提供了以下几种负载均衡方法&#xff1a; …

python接口自动化 —— 什么是接口、接口优势、类型(详解)

简介 经常听别人说接口测试&#xff0c;接口测试自动化&#xff0c;但是你对接口&#xff0c;有多少了解和认识&#xff0c;知道什么是接口吗&#xff1f;它是用来做什么的&#xff0c;测试时候要注意什么&#xff1f;坦白的说&#xff0c;笔者之前也不是很清楚。接下来先看一下…

从简历被拒到收割 8 个高薪 offer,我用了 3 个月...

半年前我一个小老弟从外包离职了&#xff0c;本以为有两年经验进个一般的公司没有问题的&#xff0c;结果人家一看是外包出来的&#xff0c;面试问的问题也不是很懂&#xff0c;简历被拒了好几次。还好这个小老弟没有气馁&#xff0c;在论坛博客和里面的大佬虚心学习&#xff0…