首页 > 其他分享 >MyBatis

MyBatis

时间:2024-09-02 19:25:05浏览次数:14  
标签:映射 List 查询 user MyBatis public

什么是框架?

框架即一个半成品软件。开发者从头开发一个软件需要花费大量精力,于是有一些项目组开发出半成品软件,开发者在这些软件的基础上进行开发,这样的软件就称之为框架。
使用框架就好比和世界上最优秀的软件工程师共同完成一个项目,并且他们完成的还是基础、全局的工作。

什么是ORM框架?

ORM(Object Relationl Mapping),对象关系映射,即在数据库和对象之间作映射处理。之前我们使用JDBC操作数据库,必须手动进行数据库和对象间的数
据转换。

// 新增方法,将对象转为sql语句字段
public void AddUser(User user) throws Exception {
 Class.forName("com.mysql.jdbc.Driver");
  Connection connection =DriverManager.getConnection("jdbc:mysql://lo
calhost:3306/mybatis?characterEncoding=utf8","root","root");
String sql = "INSERT INTO user values 
(null,?,?,?,?)";
 PreparedStatement preparedStatement = 
connection.prepareStatement(sql);
preparedStatement.setString(1,user.getName(
));
preparedStatement.setInt(2,user.getAge());
preparedStatement.setString(3,user.getAddre
ss());
preparedStatement.setString(4,user.getSex()
);
 preparedStatement.executeUpdate();
  // 省略资源关闭...
  
  // 查询方法,将数据库结果集转为对象
  public List<User> findAllUser() throws 
Exception {
Connection connection = 
DriverManager.getConnection("jdbc:mysql://lo
calhost:3306/mybatis?characterEncoding=utf8",
"root", "root");
PreparedStatement preparedStatement = 
connection.prepareStatement("select * from 
user");
ResultSet resultSet = 
preparedStatement.executeQuery();
//遍历查询结果集
List<User> users = new ArrayList<>();
while(resultSet.next()){
// 拿到每一列数据
int id = resultSet.getInt("id");
String name = 
resultSet.getString("name");
int age = resultSet.getInt("age");
String address = 
resultSet.getString("address");
String sex = 
resultSet.getString("sex");
// 将数据封装到对象中
User user = new User();
user.setId(id);
user.setName(name);
user.setAge(age);
user.setAddress(address);
user.setSex(sex);
users.add(user);
}
// 省略资源关闭...
return users;
}

这段代码中,数据库数据与对象数据的转换代码繁琐、无技术含量。而使用ORM框架代替JDBC后,框架可以帮助程序员自动进行转换,只要像平时一样操作对象,ORM框架就会根据映射完成对数据库的操作,极大的增强了开发效率。

什么是MyBatis?

在这里插入图片描述
MyBatis是一个**半自动的ORM框架,其本质是对JDBC的封装。**使用MyBatis不需要写JDBC代码,但需要程序员编写SQL语句。之前是apache的一个开源项目iBatis,2010年改名为MyBatis。

Hibernate也是一款持久层ORM框架,多年前的市场占有率很高,但近年来市场占有率越来越低。

MyBatis与Hibernate的比较

  • MyBatis是一个半自动的ORM框架,需要手写SQL语句。
  • Hibernate是一个全自动的ORM框架,不需要手写SQL语句。
  • 使用MyBatis的开发量要大于Hibernate。

为什么Hibernate市场占有率越来越低:

  • 对于新手学习Hibernate时间成本比MyBatis大很多,MyBatis上手很快。
  • Hibernate不需要写SQL语句是因为框架来生成SQL语句。对于复杂查询,开发者很难控制生
    成的SQL语句,这就导致SQL调优很难进行。
  • 之前的项目功能简单,数据量小,所以使用Hibernate可以快速完成开发。而近年来项目的数据量越来越大,而互联网项目对查询速度要求也很高,这就要求我们一定要精细化的调整SQL语句。此时灵活性更强,手动编写SQL语句的MyBatis慢慢代替了Hibernate使用。
  • 在高并发、大数据、高性能、高响应的互联网项目中,MyBatis是首选的持久框架。而对于对性能要求不高的比如内部管理系统等可以使用Hibernate。

MyBatis入门

环境搭建

  1. 将SQL文件导入数据库
  2. 创建maven工程,引入依赖
    <dependencies>
<!--        mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.5</version>
        </dependency>
<!--mysql驱动包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>
<!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
<!--       log4j-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
    </dependencies>
  1. 创建mybatis核心配置文件SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//maybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--    配置环境-->
    <environments default="mysql">
        <environment id="mysql">
<!--            事物类型-->
            <transactionManager type="JDBC"></transactionManager>
<!--            数据源-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>
</configuration>
  1. 将log4j.properties文件放入resources中,让控制台打印SQL语句。
  2. 创建实体类
public class User {
    private int id;
    private String username;
    private String sex;
    private String address;
    // 省略getter/setter/构造方法/toString方法
    }

创建持久层接口和映射文件

1.在java目录创建持久层接口

public interface UserMapper{
   List<User> findAll();
}

2. 在resource目录创建映射文件

<?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="com.xcm.mapper.UserMapper">
    <select id="findAll" resultType="com.xcm.pojo.User">select * from user</select>
</mapper>

3.将映射文件配置到mybatis核心配置文件中

 <mappers>
        <mapper resource="com/xcm/mapper/UserMapper.xml"></mapper>
    </mappers>

映射文件要和接口名称相同。
映射文件要和接口的目录结构相同。
在这里插入图片描述

  • 映射文件中namespace属性要写接口的全名
  • 映射文件中标签的id属性是接口方法的方法名
  • 映射文件中标签的resultType属性是接口方法的返回值类型
  • 映射文件中标签的parameterType属性是接口方法的参数类型
    映射文件中resultType、parameterType属性要写全类名,如果是集合类型,则写其泛型的全类名。

测试持久层接口方法

public class UserMapperTest {
    @Test
    public void testFindAll() throws Exception{
        //读取核心配置文件
        InputStream is= Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
        //SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
        SqlSessionFactory factory=builder.build(is);
        //SqlSessionFactory对象获取SqlSession对象
        SqlSession session=factory.openSession();
        //SqlSession对象获取代理对象
        UserMapper userMapper=session.getMapper(UserMapper.class);
        //代理对象执行方法
        List<User> all=userMapper.findAll();
        all.forEach(System.out::println);
        //释放资源
        session.close();
        is.close();
    }
}

MyBatis核心对象及工作流程

在这里插入图片描述

MyBatis核心对象

  • SqlSessionFactoryBuilder
    SqlSession工厂构建者对象,使用构造者模式创建SqlSession工厂对象。
  • SqlSessionFactory
    SqlSession工厂,使用工厂模式创建SqlSession对象。
  • SqlSession
    该对象可以操作数据库,也可以使用动态代理模式创建持久层接口的代理对象操作数据库
  • Mapper
    持久层接口的代理对象,他具体实现了持久层接口,用来操作数据库。

MyBatis工作流程

  • 创建SqlSessionFactoryBuilder对象
  • SqlSessionFactoryBuilder对象构建了SqlSessionFactory对象:构造者模式
  • SqlSessionFactory对象生产了SqlSession对象:工厂模式
  • SqlSession对象创建了持久层接口的代理对象:动态代理模式
  • 代理对象操作数据库

使用SqlSession操作数据库

除了代理对象能够操作数据库,SqlSession也能操作数据库。只是这种方式在开发中使用的较少,接下来我们使用SqlSession操作数据库:

@Test
    public void testFindAll2() throws Exception{
        //读取核心配置文件
        InputStream is=Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        //SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(is);
        //SqlSessionFactory对象获取SqlSession对象
        SqlSession session=sqlSessionFactory.openSession();
        //SqlSession直接操作数据库
        List<User> users=session.selectList("com.xcm.mapper.UserMapper.findAll");
        users.forEach(System.out::println);
        //关闭资源
        session.close();
        is.close();
    }

Mapper动态代理原理

获取代理对象

点开测试类的 getMapper 方法,查看该方法最终调用了什么方法。
在这里插入图片描述
当看到 Proxy.newProxyInstance 时,可以确定 getMapper 方法最终调用的是JDK动态代理方法,且使用MapperProxy类定义代理方式

查看代理方式

点开MapperProxy类,查看invoke方法,查看代理对象是如何工作的。
在这里插入图片描述
可以看到,MapperProxy调用了MapperMethod的execute方法定义了代理方式,且底层调用的是SqlSession的方法,根据映射文件标签不同调用不同的SqlSession方法。

  • SqlSession的getMapper方法,最终是调用的是JDK动态代理方法,生成一个代理对象,类型就是传入的接口类型。
  • MapperProxy对象通过调用MapperMethod的execute方法定义了代理方式,该方法的底层调用的是SqlSession的方法。

MyBatis新增

新增用户

  1. 持久层接口添加方法
 void add(User user);
  1. 映射文件添加标签
 <insert id="add" parameterType="com.xcm.pojo.User">
        insert into user(id,username,sex,address)values(#{id},#{username},#{sex},#{address})
    </insert>
  1. 编写测试方法
@Test
    public void testAdd() throws IOException {
        InputStream is=Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory= builder.build(is);
        SqlSession session=sqlSessionFactory.openSession();
        UserMapper userMapper=session.getMapper(UserMapper.class);
        User user=new User(4,"吉吉","男","狗熊岭");
        userMapper.add(user);
        //提交事物
        session.commit();
        session.close();
        is.close();
    }

当接口方法的参数类型为POJO类型时,SQL语句中绑定参数时使用 #{POJO的属性名} 即可。

MyBatis事务默认手动提交,所以在执行完增 删 改方法后,需要手动调用SqlSession对象的事务提交方法,否则数据库将不发生改变。

MyBatis修改

优化测试类

MyBatis的测试方法在操作数据库前都需要获取代理对象,操作数据库后都需要释放资源,可以利用Junit的前置后置方法,优化测试类代码。

InputStream is=null;
    SqlSession session=null;
    UserMapper userMapper=null;
    @Before
    public void before()throws IOException{
        //读取核心配置文件
        is=Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactor=sqlSessionFactoryBuilder.build(is);
        session=sqlSessionFactor.openSession();
        userMapper=session.getMapper(UserMapper.class);
    }
    @After
    public void after() throws IOException {
        session.close();
        is.close();
    }

持久层接口添加方法

void update(User user);

映射文件添加标签

 <update id="update" parameterType="com.xcm.pojo.User">
        update user
        set username =#{username},
        sex=#{sex},
        address=#{address}
        where id=#{id}
    </update>

编写测试方法

 @Before
    public void before()throws IOException{
        //读取核心配置文件
        is=Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactor=sqlSessionFactoryBuilder.build(is);
        session=sqlSessionFactor.openSession();
        userMapper=session.getMapper(UserMapper.class);
    }
    @Test
    public void testUpdate(){
        User user=new User(4,"光头强","男","狗熊岭");
        userMapper.update(user);
        session.commit();
    }
    @After
    public void after() throws IOException {
        session.close();
        is.close();
    }

MyBatis删除、根据Id查询

删除用户

1、持久层接口添加方法

 void delete(int userId);

2、映射文件添加标签

<delete id="delete" parameterType="int">
        delete from user where id=#{id}
    </delete>

当方法的参数类型是简单数据类型时,#{}中可以写任
意名称简单数据类型:基本数据类型、字符串等

3、编写测试方法

 @Test
    public void testDelete(){
        userMapper.delete(4);
        session.commit();
    }

根据ID查询用户

1、持久层接口添加方法

User findById(int id);

2、映射文件添加标签

<select id="findById" parameterType="int"
    resultType="com.xcm.pojo.User">
        select * from user where id=#{userid}
    </select>

3、编写测试方法

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

MyBatis模糊查询

使用#定义参数

1、持久层接口添加方法

List<User> findByNameLike(String username);

2、映射文件添加标签

<select id="findByNameLike" parameterType="string"
    resultType="com.xcm.pojo.User">
        select * from user where username like #{name}
    </select>

3、编写测试方法

@Test
    public void testFindByNameLike(){
        List<User> users=userMapper.findByNameLike("%熊%");
        for(User user:users){
            System.out.println(user);
        }
    }

我们看到在映射文件中,parameterType的值为 string 而没有写
java.lang.String ,这是为什么呢?参数/返回值类型为基本数据类型/包装类/String等类型时,我们可以写全类名,也可以写别名。

在这里插入图片描述
在这里插入图片描述

使用$定义参数

模糊查询如果不想在调用方法时参数加%,可以使用拼接参数的方式设置Sql:

<select id="findByUsernameLike" parameterType="string" resultType="com.xcm.pojo.User">
        select * from user where username like '%${value}%'
    </select>

测试方法写法如下:

public void testfindByUsernameLike(){
        List<User> users=userMapper.findByUsernameLike("%熊%");
        users.forEach(System.out::println);
    }

#和$的区别:

  • #表示sql模板的占位符,$表示将字符串拼接到sql模板中。
  • #可以防止sql注入,一般能用#就不用$。
  • ${}内部的参数名必须写value。

使用定义参数

如果使用#还不想在调用方法的参数中添加 %,可以使用 < bind>,< bind>允许我们在 Sql语句以外创建一个变量,并可以将其绑定到当前的 Sql语句中。用法如下:

<select id="findByNameLike1" parameterType="string" resultType="com.xcm.pojo.User">
        <bind name="likename" value="'%'+username+'%'"/>
        select * from user where username like #{likename}
    </select>

测试方法写法如下:

public void testFindByNameLike1(){
        List<User> users= userMapper.findByUsernameLike1("%熊%");
        users.forEach(System.out::println);
    }

MyBatis分页查询

分页查询时,Sql语句使用limit关键字,需要传入开始索引和每页条数两个参数。MyBatis的多参数处理有以下方式:
顺序传参
Sql中的参数使用arg0,arg1…或param1,param2…表示参数的顺序。此方法可读性较低,在开发中不建议使用。

1、持久层接口方法

/**
     * 分页查询
     * @param startIndex 开始索引
     * @param pageSize  每页条数
     * @return
     */
    List<User> findPage(int startIndex,int pageSize);

2、映射文件

<select id="findPage" resultType="com.xcm.pojo.User">
        select * from user limit #{arg0},#{arg1}
    </select>

3、测试类

@Test
    public void testFindPage(){
        List<User> users=userMapper.findPage(0,3);
        users.forEach(System.out::println);
    }

@Param传参

在接口方法的参数列表中通过@Param定义参数名称,在Sql语句中通过注解中所定义的参数名称指定参数位置。此方式参数比较直观的,推荐使用。

1、持久层接口方法

List<User> findPage2(@Param("startIndex")int startIndex,@Param("pageSize")int pageSize);

2、映射文件

<select id="findPage2" resultType="com.xcm.pojo.User">
        select * from user limit #{startIndex},#{pageSize}
    </select>

3、测试类

@Test
    public void testFindPage2(){
        List<User> users=userMapper.findPage2(5,5);
        users.forEach(System.out::println);
    }

POJO传参

自定义POJO类,该类的属性就是要传递的参数,在SQL语句中绑定参数时使用POJO的属性名作为参数名即可。此方式推荐使用。

1、自定义POJO

public class PageQuery {
    private int startIndex;
    private int pageSize;
    //省略getter/setter/构造方法
    }

2、持久层接口方法

List<User> findPage3(PageQuery pageQuery);

3、映射文件

<select id="findPage3" resultType="com.xcm.pojo.User">
        select * from user limit #{startIndex},#{pageSize}
    </select>

4、测试类

 @Test
    public void testFindPage3(){
        PageQuery pageQuery=new PageQuery(2,3);
        List<User> users=userMapper.findPage3(pageQuery);
        users.forEach(System.out::println);
    }

Map传参

如果不想自定义POJO,可以使用Map作为传递参数的载体,在SQL语句中绑定参数时使用Map的Key作为参数名即可。此方法推荐使用。

1、持久层接口方法

List<User> findPage4(Map<String,Object> params);

2、映射文件

<select id="findPage4" resultType="com.xcm.pojo.User">
        select * from user limit #{startIndex},#{pageSize}
 </select>

3、测试类

 @Test
    public void testFindPage4(){
        Map<String,Object> params=new HashMap<>();
        params.put("startIndex",2);
        params.put("pageSize",5);
        List<User> users=userMapper.findPage4(params);
        users.forEach(System.out::println);
    }

MyBatis聚合查询、主键回填

查询用户总数

1、持久层接口方法
int findCount();
2、映射文件
<select id="findCount" resultType="int">
        select count(id) from user
    </select>
3、测试类
@Test
    public void testFindCount(){
        System.out.println(userMapper.findCount());
    }

主键回填

需要获取新插入数据的主键值。如果数据库中主键是自增的,这时我们就需要使用MyBatis的主键回填功能。

1、持久层接口方法

void add1(User user);

2、映射文件

 <insert id="add1" parameterType="com.xcm.pojo.User">
<!--        keyProperty:主键属性名,keyColum:主键列名,resultType:主键类型,order:执行时机-->
        <selectKey keyProperty="id" keyColumn="id" resultType="int"
                   order="AFTER">
            SELECT LAST_INSERT_ID();
<!--            查询刚刚插入的记录的主键值,只适应于自增主键,且必须和insert语句一起执行
SELECT LAST_INSERT_ID
SELECT LAST_INSERT_ID-->
        </selectKey>
        insert into user(username,sex,address)
        values(#{username},#{sex},#{address})
    </insert>

SELECT LAST_INSERT_ID():查询刚刚插入的记录的主键值,只适用于自增主键,且必须和insert语句一起执行。

3、测试类

 @Test
    public void testAdd1(){
        User user=new User( "占星魔仙","女", "魔仙堡");
        userMapper.add1(user);
        session.commit();
        System.out.println(user.getId());
    }

MyBatis配置文件_< properties>

MyBatis配置文件结构:

在这里插入图片描述

properties

属性值定义。properties标签中可以定义属性值,也可以引入外部配置文件。无论是内部定义还是外部引入,都可以使用${name}获取值。
例如:我们可以将数据源配置写到外部的db.properties中,再使用properties标签引入外部配置文件,这样可以做到动态配置数据源。

1、编写db.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db1
jdbc.username=root
jdbc.password=1234

2、在配置文件中引入db.properties

也可以将数据源数据通过 < properties>配置到MyBatis配置文件内,但这样做没什么意义。

  <properties resource="db.properties"></properties>
<!--    配置环境-->
    <environments default="mysql">
        <environment id="mysql">
<!--            事物类型-->
            <transactionManager type="JDBC"></transactionManager>
<!--            数据源-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
MyBatis配置文件_< settings>

< settings>是配置MyBatis运行时的一些行为的,例如缓存、延迟加载、命名规则等一系列控制性参数。后期我们会使用该标签配置缓存和延迟加载等。

MyBatis配置文件_< plugins>

< plugins>是配置MyBatis插件的。插件可以增强MyBatis功能,比如进行sql增强,打印日志,异常处理等。后期我们会使用该标签配置分页插件。

MyBatis配置文件_< typeAliases>

MyBatis对常用类有默认别名支持,比如java.lang.Stirng的别名为string。除此之外,我们也可以使用 < typeAliases>设置自定义别名。

为一个类配置别名
<typeAliases>
        <typeAlias type="全类名" alias="别名">
        </typeAlias>
    </typeAliases>

此时我们即可在映射文件中使用自定义别名,如:

1、配置文件:
 <typeAliases>
        <typeAlias type="com.xcm.pojo.User" alias="User">
        </typeAlias>
    </typeAliases>
2、映射文件:
<select id="findAll" resultType="User">
        select * from user
    </select>

为一个所有包下的所有类配置别名

<typeAliases>
        <package name="包名"/>
    </typeAliases>

此时该包下所有的类都有了别名,别名省略包名,和类名相同。如:

1、配置文件:

<typeAliases>
        <package name="com.xcm.pojo"/>
    </typeAliases>

2、映射文件:

<select id="findPage1" resultType="User">
        select * from user limit #{param1},#{param2}
    </select>

MyBatis配置文件_< environments>

< environments>可以为MyBatis配置数据环境。

事务管理

<environments default="mysql">
        <environment id="mysql">
<!--            JDBC:使用JDBC的提交和回滚,MANAGED:不做事务处理-->
            <transactionManager type="JDBC"></transactionManager>
        </environment>
    </environments>

连接池

 <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
<!--            连接池设置-->
            <dataSource type="POOLED"></dataSource>
<!--            数据源设置...-->
        </environment>
    </environments>

dataSource的type属性:

  • POOLED:使用连接池管理连接,使用MyBatis自带的连接池。
  • UNPOOLED:不使用连接池,直接由JDBC连接。
  • JNDI:由JAVAEE服务器管理连接,如果使用Tomcat作为服务器则使用Tomcat自带的连接池管理。

MyBatis配置文件_< mappers>

< mappers>用于注册映射文件或持久层接口,只有注册的映射文件才能使用,共有四种方式都可以完成注册:

  1. 使用相对路径注册映射文件
<mappers>
        <mapper resource="com/xcm/mapper/UserMapper.xml"/>
    </mappers>
  1. 使用绝对路径注册映射文件
 <mappers>
        <mapper url="file:///D:\mybatis_demo\src\main\resources\com\xcm\mapper\UserMapper.xml"/>
    </mappers>
  1. 注册持久层接口
 <mappers>
        <mapper class="com.xcm.mapper.UserMapper"/>
    </mappers>
  1. 注册一个包下的所有持久层接口
<mappers>
        <package name="com.xcm.mapper"/>
    </mappers>

MyBatis映射文件_< resultMap>

MyBatis映射文件中除了 < insert>、< delete>、< update>、< select>外,还有一些标签可以使用:

resultMap

标签的作用的自定义映射关系。
MyBatis可以将数据库结果集封装到对象中,是因为结果集的列名和对象属性名相同:
在这里插入图片描述
当POJO属性名和数据库列名不一致时,MyBatis无法自动完成映射关系。如:
在这里插入图片描述
此时有两种解决方案:

  • Sql语句的查询字段起与POJO属性相同的别名。
<select id="findAll" resultType="com.xcm.pojo.Teacher">
    select tid as id,tname as teacherName from teacher;
</select>
  • 自定义映射关系
  1. 在映射文件中,使用 < resultMap> 自定义映射关系:
<resultMap id="teacherMapper" type="com.xcm.pojo.Teacher">
<!--    id定义主键列 property:POJO属性名 column:数据库列名-->
    <id property="id" column="tid"></id>
<!--    result定义普通列  property:POJO属性名  column:数据库列名-->
    <result property="teacherName" column="tname"></result>
</resultMap>
  1. 在 < select> 标签中,使用 resultMap 属性代替 resultType 属性,使用自定义映射关系。
<select id="findAll" resultMap="teacherMapper">
    select * from teacher
</select>

MyBatis映射文件_< sql>、< include>

< sql> 用来定义可重用的Sql片段,通过 < include> 引入该片段。如:Sql语句的查询字段起与POJO属性相同的别名,该Sql片段就可以重用。

<sql id="selectAllField">
    select tid as id,tname as teacherName
</sql>
<select id="findAll" resultType="com.xcm.pojo.Teacher">
    <include refid="selectAllField"></include>
    from teacher
</select> 
    <select id="findById" resultType="com.xcm.pojo.Teacher">
        <include refid="selectAllField"></include>
        from teacher where tid=#{id}
    </select>

MyBatis映射文件_特殊字符处理

在Mybatis映射文件中尽量不要使用一些特殊字符,如: < , > 等。
我们可以使用符号的实体来表示:

符号实体
<&lt;
大于(>)& gt;
&& amp;
& apos;
"& quot;
<select id="findById" parameterType="int"
    resultType="com.xcm.pojo.User">
        select * from user where id &gt; #{userid}
    </select>

动态SQL_

在这里插入图片描述
一个查询的方法的Sql语句不一定是固定的。比如电商网站的查询商品,用户使用不同条件查询,Sql语句就会添加不同的查询条件。此时就需要在方法中使用动态Sql语句。

< if>

< if> 标签内的Sql片段在满足条件后才会添加,用法为: < if test=“条
件”> 。例如:根据不同条件查询用户:

  1. 持久层接口添加方法
 //用户通用查询
    List<User> findByCondition(User user);
  1. 映射文件添加标签
<select id="findByCondition" parameterType="User" resultType="User">
    select * from user where 1=1
    <if test="username !=null and username.length()!=0">
        and username like #{username}
    </if>
    <if test="sex!=null and sex.length()!=0">
        and sex = #{sex}
    </if>
    <if test="address!=null and address.length()!=0">
        and address=#{address}
    </if>
</select>
  1. 编写测试方法
@Test
    public void testFindByCondition(){
        User user=new User();
        List<User> users=userMapper.findByCondition(user);
        users.forEach(System.out::println);
        user.setUsername("%熊大%");
        List<User> users1=userMapper.findByCondition(user);
        users1.forEach(System.out::println);
        user.setAddress("狗熊岭");
        List<User> users2=userMapper.findByCondition(user);
        users2.forEach(System.out::println);
    }

if中的条件不能使用&&/||,而应该使用and/or
if中的条件可以直接通过属性名获取参数POJO的属性值,并且该值可以调用方法。

where后为什么要加1=1?
任意条件都可能拼接到Sql中。如果有多个条件,从第二个条件开始前都需要加And关键字。加上1=1这个永久成立的条件,就不需要考虑后面的条件哪个是第一个条件,后面的条件前都加And关键字即可。

动态SQL_< where>

< where> 可以代替sql中的where 1=1 和第一个and,更符合程序员的开发习惯,使用 < where> 后的映射文件如下:

<select id="findByCondition1" resultType="User" parameterType="User">
    select * from user
    <where>
        <if test="username != null and username.lemgth()!=0">
            username like #{username}
        </if>
        <if test="sex !=null and sex.length()!=0">
            and sex=#{sex}
        </if>
    </where>
</select>

动态SQL_< set>

< set> 标签用在update语句中。借助 < if> ,可以只对有具体值的字段进行更新。 < set> 会自动添加set关键字,并去掉最后一个if语句中多余的逗号。

<update id="update" parameterType="User">
     update user
     <set>
         <if test="username !=null and username.length()>0">
             username = #{username},
         </if>
         <if test="sex !=null and sex.length()>0">
             sex = #{sex},
         </if>
     </set>
     <where>
         id = #{id}
     </where>
 </update>

动态SQL_< choose>、< when>、< otherwise>

这些标签表示多条件分支,类似JAVA中的 switch…case 。 < choose> 类似switch , < when> 类似 case , < otherwise> 类似 default ,用法如下:

<select id="findByCondition2" parameterType="User" resultType="User">
        select * from user
        <where>
            <choose>
                <when test="username.length() &lt; 5">
                    username like #{username}
                </when>
                <when test="username.length() &lt; 10">
                    username = #{username}
                </when>
                <otherwise>
                    id=1
                </otherwise>
            </choose>
        </where>
    </select>

这段代码的含义为:用户名<5时使用模糊查询,用户名>=5并且<10时使用精确查询,否则查询id为1的用户。

动态SQL_< foreach>

< foreach> 类似JAVA中的for循环,可以遍历集合或数组。 < foreach> 有如下属性:

  • collection:遍历的对象类型
  • open:开始的sql语句
  • close:结束的sql语句
  • separator:遍历每项间的分隔符
  • item:表示本次遍历获取的元素,遍历List、Set、数组时表示每项元素,遍历map时表示键值对的值。
  • index:遍历List、数组时表示遍历的索引,遍历map时表示键值对的键。

遍历数组

使用 < foreach> 遍历数组进行批量删除。

  • 持久层接口添加方法
//使用foreach遍历数组进行删除
    void deleteBatch(int[] ids);
  • 映射文件添加标签
<delete id="deleteBatch" parameterType="int">
        delete from user
        <where>
            <foreach open="id in(" close=")" separator="," collection="array"
                     item="id">
                #{id}
            </foreach>
        </where>
    </delete>
  • 编写测试方法
    @Test
    public void testDeleteBatch(){
        int[] ids={1,2};
        userMapper.deleteBatch(ids);
        session.commit();
    }

遍历Collection

< foreach> 遍历List和Set的方法是一样的,我们使用 < foreach> 遍历List进行批量添加。

  • 持久层接口添加方法
void insertBatch(List<User> users);
  • 映射文件添加标签
<insert id="insertBatch" parameterType="User">
        insert into user values
        <foreach collection="list" item="user" separator=",">
            (null,#{user.username},#{user.sex},#{user.address})
        </foreach>
    </insert>
  • 编写测试方法
  @Test
    public void testInsertBatch(){
        User user1=new User("占星魔仙","女","魔仙堡");
        User user2=new User("小灰灰","男","青青草原");
        User user3=new User("佟湘玉","女","同福客栈");
        List<User> list=new ArrayList<>();
        list.add(user1);
        list.add(user2);
        list.add(user3);
        userMapper.insertBatch(list);
        session.commit();

    }

遍历Map

使用 < foreach> 遍历Map进行多条件查询。

  • 持久层接口添加方法
/**
     * 多条件查询
     * @param map  查询条件键值对 键:属性名  值:属性值
     * @return
     */
    List<User> findUser(@Param("queryMap")Map<String,Object> map);
  • 映射文件添加标签
<select id="findUser" parameterType="map" resultType="User">
        select * from user
        <where>
            <foreach collection="queryMap" separator="and" index="key" item="value">
                ${key}=#{value}
            </foreach>
        </where>
    </select>
  • 编写测试方法
 @Test
    public void testFindUser(){
        Map<String,Object> queryMap=new HashMap<>();
        queryMap.put("sex","男");
        queryMap.put("address","青青草原");
        List<User> users=userMapper.findUser(queryMap);
        users.forEach(System.out::println);
    }

缓存介绍

在这里插入图片描述
缓存是内存当中一块存储数据的区域,目的是提高查询效率。
MyBatis会将查询结果存储在缓存当中,当下次执行相同的SQL时不访问数据库,而是直接从缓存中获取结果,从而减少服务器的压力。

  • 什么是缓存?

存在于内存中的一块数据。

  • 缓存有什么作用?

减少程序和数据库的交互,提高查询效率,降低服务器和数据库的压力。

  • 什么样的数据使用缓存?

经常查询但不常改变的,改变后对结果影响不大的数据。

  • MyBatis缓存分为哪几类?

一级缓存和二级缓存

  • 如何判断两次Sql是相同的?

查询的Sql语句相同
传递的参数值相同
对结果集的要求相同
预编译的模板Id相同

MyBatis一级缓存

在这里插入图片描述

  • MyBatis一级缓存也叫本地缓存。SqlSession对象中包含一个Executor对象,Executor对象中包含一个PerpetualCache对象,在该对象存放一级缓存数据。
  • 由于一级缓存是在SqlSession对象中,所以只有使用同一个SqlSession对象操作数据库时才能共享一级缓存。
  • MyBatis的一级缓存是默认开启的,不需要任何的配置。

测试一级缓存

//相同SqlSession
 @Test
    public void testCache1() throws IOException{
       InputStream is=Resources.getResourceAsStream("SqlMapConfig.xml");
       SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
       SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(is);
       SqlSession session=sqlSessionFactory.openSession();
       //使用同一个SqlSession查询
        UserMapper mapper=session.getMapper(UserMapper.class);
        UserMapper mapper1=session.getMapper(UserMapper.class);
        User user=mapper.findById(1);
        System.out.println(user.hashCode());
        System.out.println("-----------------------------------");
        User user2=mapper1.findById(1);
        System.out.println(user2.hashCode());
    }
    //不同SqlSession
    @Test
    public void testCache2() throws IOException{
        InputStream is=Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(is);
        SqlSession session1=sqlSessionFactory.openSession();
        SqlSession session2=sqlSessionFactory.openSession();
        //使用不同的SqlSession查询
        UserMapper userMapper=session1.getMapper(UserMapper.class);
        UserMapper userMapper1=session2.getMapper(UserMapper.class);
        User user1=userMapper.findById(1);
        System.out.println(user1.hashCode());
        System.out.println("----------------------------------------");
        User user2=userMapper1.findById(1);
        System.out.println(user2.hashCode());

    }

MyBatis清空一级缓存

进行以下操作可以清空MyBatis一级缓存:

  • SqlSession 调用 close() :操作后SqlSession对象不可用,该对象的缓存数据也不可用。
  • SqlSession 调用 clearCache() / commit() :操作会清空一级缓存数据。
  • SqlSession 调用增删改方法:操作会清空一级缓存数据,因为增删改后数据库发生改变,缓存数据将不准确。
 @Test
    public void testCache3() throws IOException{
        InputStream inputStream=Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(inputStream);
        SqlSession session=sqlSessionFactory.openSession();
        UserMapper userMapper1=session.getMapper(UserMapper.class);
        UserMapper userMapper2=session.getMapper(UserMapper.class);
        User user1=userMapper1.findById(1);
        System.out.println(user1.hashCode());
        userMapper1.delete(2);
//        session.close();
//        session.clearCache();
//        session.commit();
        System.out.println("------------------------------------");
        User user2=userMapper2.findById(1);
        System.out.println(user2.hashCode());
    }

MyBatis二级缓存

在这里插入图片描述

  • MyBatis二级缓存也叫全局缓存。数据存放在SqlSessionFactory中,只要是同一个工厂对象创建的SqlSession,在进行查询时都能共享数据。 一般在项目中只有一个SqlSessionFactory对象,所以二级缓存的数据是全项目共享的。
  • MyBatis一级缓存存放的是对象,二级缓存存放的是对象的数据。所以要求二级缓存存放的POJO必须是可序列化的,也就是要实现Serializable接口
  • MyBatis二级缓存默认不开启,手动开启后数据先存放到一级缓存中,只有一级缓存数据清空后,数据才会存放到二级缓存中。

SqlSession调用clearCache()无法将数据存到二级缓存中

开启二级缓存

  • POJO类实现Serializable接口。
public class User implements Serializable {
    private int id;
    private String username;
    private String sex;
    private String address;
    }
  • 在MyBatis配置文件添加如下设置:
<settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
    //由于cacheEnabled默认值是true,所以该设置可以省略
  • 在映射文件添加 < cache /> 标签,该映射文件下的所有方法都支持二级缓存。
//如果查询到的集合中对象过多,二级缓存只能缓存1024个对象引用。
 <cache size="2048"/>
  • 测试二级缓存
    @Test
    public void testCache4() throws IOException{
        InputStream is=Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(is);
        SqlSession session1=sqlSessionFactory.openSession();
        SqlSession session2=sqlSessionFactory.openSession();
        UserMapper mapper1=session1.getMapper(UserMapper.class);
        UserMapper mapper2=session2.getMapper(UserMapper.class);
        User user1=mapper1.findById(1);
        System.out.println(user1);
        System.out.println(user1.hashCode());
        //让一级缓存失效
        session1.commit();
        System.out.println("--------------------------------");
        User user2=mapper2.findById(1);
        System.out.println(user2);
        System.out.println(user2.hashCode());
    }

MyBatis关联查询

MyBatis的关联查询分为一对一关联查询和一对多关联查询。

  • 查询对象时,将关联的另一个对象查询出来,就是一对一关联查询。
  • 查询对象时,将关联的另一个对象的集合查询出来,就是一对多关联查询。

例如有学生类和班级类:
一个学生对应一个班级,也就是学生类中有一个班级属性,这就是一对一关系。

一个班级对应多个学生,也就是班级类中有一个学生集合属性,这就是一对多关系。

实体类设计如下:

public class Student {
    private int sid;
    private String sex;
    private Classes classes;
    //省略getter/setter/toString
}

public class Classes {
    private int cid;
    private String className;
    private List<Student> studentList;
    // 省略getter/setter/toString
}

数据库设计如下:
在这里插入图片描述

MyBatis一对一关联查询

查询学生时,将关联的一个班级对象查询出来,就是一对一关联查询。

创建持久层接口

List<Student> findAll();

创建映射文件

<mapper namespace="com.xcm.mapper.StudentMapper">
    <resultMap id="studentMapper" type="Student">
<!--        主键列-->
       <id property="sid" column="sid"></id>
<!--        普通列-->
        <result property="name" column="name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>

<!--    一对一对象列 property:属性名  column:关键列名 javaType:对象类型-->
    <association property="classes" column="classId" javaType="Classes">
<!--        关键对象主键列-->
        <id property="cid" column="cid"></id>
<!--        关键对象普通列-->
        <result property="className" column="className"></result>
    </association>
    </resultMap>
<!--    多表查询,级联查询学生和其班级-->
    <select id="findAll" resultMap="studentMapper">
        select * from student left join classes on student.classId=
        classes.cid;
    </select>
</mapper>

配置文件注册映射文件

 <mapper resource="com/xcm/mapper/StudentMapper.xml"/>

测试一对一关联查询

public class StudentMapperTest {
    InputStream is=null;
    SqlSession session=null;
    @Before
    public void before() throws IOException{
        is= Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(is);
        session=sqlSessionFactory.openSession();
    }
      @Test
      public void testFindAllStudent() {
          StudentMapper studentMapper = session.getMapper(StudentMapper.class);
          List<Student> all = studentMapper.findAll();
          all.forEach(System.out::println);
      }
        @After
        public void after() throws IOException{
           session.close();
           is.close();
        }
    }

MyBatis一对多关联查询

查询班级时,将关联的学生集合查询出来,就是一对多关联查询。

  • 创建持久层接口
public interface classesMapper {
    List<Classes> findAllClasses();
}
  • 创建映射文件
<mapper namespace="com.xcm.mapper.classesMapper">
    <resultMap id="classesMapper1" type="Classes">
<!--        主键列-->
       <id property="cid" column="cid"></id>
<!--        普通列-->
        <result property="className" column="className"/>
<!--        集合列 property:属性名 column:关联列名 ofType:集合的泛型-->
        <collection property="studentList" column="classId" ofType="Student">
            <id property="classId" column="classId"></id>
            <result property="age" column="age"/>
            <result property="sex" column="sex"/>
            <result property="name" column="name"/>
            <result property="sid" column="sid"/>

        </collection>
    </resultMap>
<!--    多表查询,级联查询班级和它的学生-->
    <select id="findAllClasses" resultMap="classesMapper1">
        select * from classes left join student on classes.cid=student.classId;
    </select>
</mapper>
  • 测试一对多关联查询
public class classesMapperTest {
    InputStream is=null;
    SqlSession session=null;

    @Before
    public void before() throws IOException{
        is= Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(is);
        session=sqlSessionFactory.openSession();
    }
      @Test
      public void testFindAllClasses() {
         classesMapper classesMapper =session.getMapper(classesMapper.class);
         List<Classes> all=classesMapper.findAllClasses();
         all.forEach(System.out::println);
      }

        @After
        public void after() throws IOException{
           session.close();
           is.close();
        }
    }

MyBatis多对多关联查询

MyBatis多对多关联查询本质就是两个一对多关联查询
例如有老师类和班级类:
一个老师对应多个班级,也就是老师类中有一个班级集合属性。
一个班级对应多个老师,也就是班级类中有一个老师集合属性。
在这里插入图片描述
在这里插入图片描述

接下来测试查询老师时,将关联的班级集合查询出来。

  • 创建持久层接口
public interface TeacherMapper {
    List<Teacher> findAllTeacher();
  
}
  • 创建映射文件
<mapper namespace="com.xcm.mapper.TeacherMapper">
    <resultMap id="teacherMapper" type="Teacher">
        <id column="tid" property="tid"></id>
        <result column="tname" property="tname"/>
        <collection property="classes" column="tid" ofType="Classes">
            <id column="cid" property="cid"></id>
            <result column="className" property="className"></result>
        </collection>
    </resultMap>
    <select id="findAllTeacher" resultMap="teacherMapper">
        select * from teacher
        left join classes_teacher
        on teacher.tid=classes_teacher.tid
        left join classes
        on classes_teacher.cid=classes.cid
    </select>
  </mapper>
  • 测试多对多关联查询
    @Test
    public void testFindAllTeacher(){
        TeacherMapper teacherMapper=session.getMapper(TeacherMapper.class);
        List<Teacher> all1=teacherMapper.findAllTeacher();
        all1.forEach(System.out::println);
    }

结果如下图所示:

在这里插入图片描述
如果想查询班级时,将关联的老师集合查询出来,只需要修改班级映射文件的Sql语句和 < resultMap> 即可:

  • 创建持久层接口
public interface TeacherMapper {
    List<Classes> findAll();
}

  • 创建映射文件
 <resultMap id="classesMapper" type="Classes">
        <id property="cid" column="cid"/>
        <result property="className" column="className"/>
<!--        集合列 property:属性名 column:关联列名 ofType:集合的泛型-->
        <collection property="studentList" column="classId" ofType="Student">
            <id property="sid" column="sid"/>
            <result property="name" column="name"/>
            <result property="age" column="age"/>
            <result property="sex" column="sex"/>
        </collection>
        <collection property="teacherList" column="cid" ofType="Teacher">
            <id property="tid" column="tid"/>
            <result property="tname" column="tname"/>
        </collection>
    </resultMap>
    <select id="findAll" resultMap="classesMapper">
        select * from classes
        left join student
        on classes.cid=student.classId
        left join  classes_teacher
        on classes.cid=classes_teacher.cid
        left join teacher
        on classes_teacher.tid=teacher.tid
    </select>
     </mapper>
  • 测试多对多关联查询
    @Test
    public void testFindAllTeacher(){
        TeacherMapper teacherMapper=session.getMapper(TeacherMapper.class);
        List<Classes> all=teacherMapper.findAll();
        all.forEach(System.out::println);
    }

在这里插入图片描述

MyBatis分解式查询_一对多

在MyBatis多表查询中,使用连接查询时一个Sql语句就可以查询出所有的数据。如:

# 查询班级时关联查询出学生
select * from classes 
left join student
on student.classId=classes.cid

也可以使用分解式查询,即将一个连接Sql语句分解为多条Sql语句,如:

# 查询班级时关联查询出学生
select * from classes;
select * from student where classId=1;
select * from studnet where classId=2;

这种写法也叫N+1查询。

连接查询:
优点:降低查询次数,从而提高查询效率。
缺点:如果查询返回的结果集较多会消耗内存空间。

N+1查询:
优点:结果集分步获取,节省内存空间。
缺点:由于需要执行多次查询,相比连接查询效率低。

以查询班级时关联查询出学生为例,使用N+1查询:
创建每个查询语句的持久层方法

public interface classesMapper {
// 查询所有班级
    List<Classes> findAllClasses1();
}
public interface StudentMapper {
   // 根据班级Id查询学生
    List<Student> findByClassId(int classId);
}

在映射文件中进行配置

<select id="findAllClasses1" resultType="Classes">
        select * from classes
    </select>

<select id="findByClassId" resultType="Student" parameterType="int">
        select * from student where classId=#{classId}
    </select>

修改主表映射文件中的查询方法

<resultMap id="MyClassesMapper" type="Classes">
        <id property="cid" column="cid"/>
        <result property="className" column="className"/>
        <collection property="studentList" ofType="Student"
                    select="com.xcm.mapper.StudentMapper.findByClassID" column="cid">

        </collection>
    </resultMap>

测试查询方法

@Test
     public void testFindAllClasses2(){
        classesMapper MyClassesMapper=session.getMapper(classesMapper.class);
        List<Classes> all=MyClassesMapper.findAllClasses1();
        all.forEach(System.out::println);
     }

MyBatis分解式查询_一对一

查询学生时关联查询出班级也可以使用分解式查询,首先将查询语句分开:

select * from student;
select * from classes where cid=?;

创建每个查询语句的持久层方法

public interface classesMapper2 {
   //根据ID查询班级
    Classes findByCid(int cid);
}

public interface StudentMapper2 {
    //查询所有学生
    List<Student> findAll();
}

在映射文件中进行配置

<mapper namespace="com.xcm.mapper2.classesMapper2">
 <select id="findByCid" resultType="Classes" parameterType="int">
     select * from classes where cid =#{cid}
 </select>
</mapper>

<mapper namespace="com.xcm.mapper2.StudentMapper2">
    <select id="findAll" resultType="Student" resultMap="MyStudentMapper">
        select * from student
    </select>
    <resultMap id="MyStudentMapper" type="Student">
        <id property="sid" column="sid"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <association property="classes" javaType="Classes" select="com.xcm.mapper2.classesMapper2.findByCid" column="classId">

        </association>
    </resultMap>
</mapper>

测试查询方法

public class testFindAllStudent2 {
    InputStream is=null;
    SqlSession session=null;
    @Before
    public void before() throws IOException {
        is= Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(is);
        session=sqlSessionFactory.openSession();
    }
    @Test
    public void testFindAllStudent2(){
        StudentMapper2 MyStudentMapper2=session.getMapper(StudentMapper2.class);
        List<Student> all=MyStudentMapper2.findAll();
        all.forEach(System.out::println);
    }
    @After
    public void after() throws IOException {
        session.close();
        is.close();
    }

}

N+1查询与连接查询相比,需要在 < collection> 或 < association> 中添加select属性

<association property="classes" javaType="Classes" select="com.xcm.mapper2.classesMapper2.findByCid" column="classId">
</association>
    <association property="classes" column="classId" javaType="Classes">
        <id property="cid" column="cid"></id>
        <result property="className" column="className"></result>
    </association>

MyBatis延迟加载

分解式查询又分为两种加载方式:

  • 立即加载:在查询主表时就执行所有的Sql语句。
  • 延迟加载:又叫懒加载,首先执行主表的查询语句,使用从表数据时才触发从表的查询语句。

延迟加载在获取关联数据时速度较慢,但可以节约资源,即用即
取。

开启延迟加载

  • 设置所有的N+1查询都为延迟加载:
 <settings>
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>
  • 设置某个方法为延迟加载:
    在 < association> 、 < collection> 中添加fetchType属性设置加载方式。
    lazy:延迟加载;eager:立即加载。

测试延迟加载

 @Test
    public void testFindAllClasses2(){
        classesMapper2 classesMapper21=session.getMapper(classesMapper2.class);
        List<Classes> all=classesMapper21.findByAll();
        all.forEach(System.out::println);
        System.out.println("===================================================");
        System.out.println(all.get(0).getStudentList());
    }

由于打印对象时会调用对象的 toString 方法, toString 方法默认会触发延
迟加载的查询,所以我们无法测试出延迟加载的效果

在配置文件设置lazyLoadTriggerMethods属性,该属性指定对象的什么方法触发延迟加载,设置为空字符串即可。

<settings>
    <setting name="lazyLoadTriggerMethods" value=""/>
</settings>

一般情况下,一对多查询使用延迟加载,一对一查询使用立即加载。

MyBatis注解开发_环境搭建

MyBatis可以使用注解替代映射文件。映射文件的作用就是定义Sql
语句,可以在持久层接口上使用@Select/@Delete/@Insert/@Update定义Sql语句,这样就不需要使用映射文件了。

  • 1、创建maven工程,引入依赖
  • 2、创建db.properties配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db1
jdbc.username=root
jdbc.password=1234
  • 3、创建mybatis核心配置文件SqlMapConfig.xml
<configuration>
 <properties>db.properties</properties>
 <environments default="mysql">
 <environment id="mysql">
   <transactionManager type="JDBC"></transactionManager>
   <dataSource type="POOLED">
       <property name="driver" value="${jdbc.driver}"/>
       <property name="url" value="${jdbc.url}"/>
       <property name="username" value="${jdbc.username}"/>
       <property name="password" value="${jdbc.password}"/>
   </dataSource>
 </environment>
 </environments>
 <mappers>
<package name="com.xyy.mapper"/>
</mappers>
</configuration>
  • 4、将log4j.properties文件放入resources中,让控制台打印SQL语句。
  • 5、创建实体类。
  • 6、创建持久层接口,并在接口方法上定义Sql语句。
public interface UserMapper{
   @Select(select * from user)
   List<User> findAll();
}

由于注解在方法上方,而方法中就有参数类型和返回值类
型,所以使用注解开发不需要定义参数类型和返回值类型

  • 7、在核心配置文件注册持久层接口,由于没有映射文件,所以只能
    采用注册接口或注册包的方法。
<mappers>
<package name="com.xyy.mapper"/>
</mappers>
  • 8、测试方法
public class TestFindByAll {
    InputStream is=null;
    SqlSession session=null;
    UserMapper userMapper=null;
    @Before
    public void before() throws IOException {
      is= Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(is);
       session=sqlSessionFactory.openSession();
       userMapper=session.getMapper(UserMapper.class);
    }
    @Test
    public void testFindAll(){
        List<User> all=userMapper.findAll();
        all.forEach(System.out::println);
    }
    @After
    public void after() throws IOException {
        is.close();
        session.close();
    }
}

MyBatis注解开发_增删改查

持久层代码

public interface UserMapper {
    @Select("select * from user")
    List<User> findAll();

     //@SelectKey注解是插入数据时进行自增,keyColumn是数据库中id列名,keyProperty是对应实体中属性名
    @SelectKey(keyColumn = "id",keyProperty = "id",resultType = int.class,before=false,
    statement = "SELECT LAST_INSERT_ID()")
    @Insert("insert into user(username,sex,address) VALUES (#{username},#{sex},#{address})")
    void add(User user);

    @Update("update user set username=#{username},sex=#{sex},address=#{address} " +
            "where id=#{id}")
    void update(User user);

    @Delete("delete from user where id=#{id}")
    void delete(int id);

    @Select("select * from user where username like #{username}")
    List<User> findByUsernameLike(String username);

}

MyBatis注解开发_动态Sql

MyBatis注解开发中有两种方式构建动态Sql:

使用脚本标签

将Sql嵌套在 < script> 内即可使用动态Sql标签:

<script>
   select * from user
<where>
   <if test="username !=null and username.length()!=0">
     username like #{username}
</if>
<if test="sex != null and sex.length()!=0">
     and sex=#{sex}
</if>
<if test="address !=null and address.length()!=0">
  and address=#{address}
</if>
</where>
</script>
 //根据任意条件查询
    @Select("<script>\n" +
            "   select * from user\n" +
            "<where>\n" +
            "   <if test=\"username !=null and username.length()!=0\">\n" +
            "     username like #{username}\n" +
            "</if>\n" +
            "<if test=\"sex != null and sex.length()!=0\">\n" +
            "     and sex=#{sex}\n" +
            "</if>\n" +
            "<if test=\"address !=null and address.length()!=0\">\n" +
            "  and address=#{address}\n" +
            "</if>\n" +
            "</where>\n" +
            "</script>")
    List<User> findByCondition(User user);

在方法中构建动态Sql

在MyBatis中有 @SelectProvider 、 @UpdateProvider 、 @DeleteProvider 、@InsertProvider 注解。当使用这些注解时将不在注解中直接编写SQL,
而是调用某个类的方法来生成SQL。


//生成任意条件查询的Sql语句
public String findByConditionSql(User user){
     StringBuffer sb=new StringBuffer("select * from user where 1=1");
     if(user.getUsername()!=null && user.getUsername().length()!=0){
           sb.append("and username like #{username}");
      }
      if(user.getSex()!=null && user.getSex().length()!=0){
            sb.append("and sex = #{sex}");

      }
      if(user.getAddress()!=null && user.getAddress().length()!=0){
            sb.append("and address=#{address}");
}
      return sb.toString();

}

MyBatis注解开发_自定义映射关系

当POJO属性名与数据库列名不一致时,需要自定义实体类和结果集
的映射关系,在MyBatis注解开发中,使用 @Results 定义并使用自定
义映射,使用 @ResultMap 使用自定义映射
,用法如下:

//查询所有用户
    @Results(id="userDiyMapper",value={
            @Result(id=true,property = "id",column = "id"),
            @Result(property = "username",column = "username1"),
            @Result(property = "sex",column = "sex1"),
            @Result(property = "address",column = "address1"),
    })
    @Select("select * from user")
    List<User> findAll();
//根据id查询
    @ResultMap("userDiyMapper")
    @Select("select * from user where id=#{id}")
    User findById(int id);

MyBatis注解开发_二级缓存

MyBatis默认开启一级缓存,接下来我们学习如何在注解开发时使用二级缓存:

  • POJO类实现Serializable接口。
    在这里插入图片描述

  • 在MyBatis配置文件添加如下设置:

<settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
  • 在持久层接口上方加注解
    @CacheNamespace(blocking=true),该接口的所有方法都支持二级缓存。
    在这里插入图片描述

  • 测试二级缓存

@Test
    public void testCache() throws IOException {

        is=Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(is);
        SqlSession session2=sqlSessionFactory.openSession();
        SqlSession session3=sqlSessionFactory.openSession();
        User user1=session2.getMapper(UserMapper.class).findById(1);
        System.out.println(user1);
        System.out.println(user1.hashCode());
        session2.commit();//清空一级缓存,将数据存到二级缓存
        User user2=session3.getMapper(UserMapper.class).findById(1);
        System.out.println(user2);
        System.out.println(user2.hashCode());


    }

MyBatis注解开发_一对一关联查询

在MyBatis的注解开发中对于多表查询只支持分解查询,不支持连
接查询。

  1. 创建实体类
public class Classes {
    private int cid;
    private String className;
    private List<Student> studentList;
    // 省略getter/setter/toString
    }
    
    public class Student {
    private int sid;
    private String name;
    private int age;
    private String sex;
    private int classId;
    private Classes classes;
    // 省略getter/setter/toString
}
  1. 创建分解后的查询方法
public interface StudentMapper {
    @Select("select * from student")
    List<Student> findAll();
}

public interface classesMapper {
    //根据id查询班级
    @Select("select * from classes where cid=#{cid}")
    Classes findByCid(Integer cid);
}

  1. 主表的查询配置自定义映射关系
 @Select("select * from student")
    //自定义映射问题
    @Results(id="studentMapper",value = {
            @Result(id=true,property = "sid",column = "sid"),
            @Result(property = "name",column = "name"),
            @Result(property = "age",column = "age"),
            @Result(property = "sex",column = "sex"),
            /**
             * property:属性名
             * column:调用从表方法时传入的参数列
             * one:表示该属性是一个对象
             * select:调用的从表方法
             * fetchType:加载方式
             */
            @Result(property = "classes",column = "classId",
            one=@One(select = "com.xyy.mapper.classesMapper.findByCid",fetchType =
            FetchType.EAGER))
    }
    )
    List<Student> findAll();
  1. 测试
  @Test
    public void findAllStudent(){
        StudentMapper studentMapper=session.getMapper(StudentMapper.class);
        List<Student> all=studentMapper.findAll();
        all.forEach(System.out::println);
    }

MyBatis注解开发_一对多关联查询

  1. 创建分解后的查询方法
public interface classesMapper {
    //查询所有班级
    @Select("select * from classes")
    List<Classes> findAll();
}

public interface StudentMapper {
    //根据班级id查询学生
    @Select("select * from student where classId=#{classId}")
    List<Student> findByClassId(int classId);
}
  1. 主表的查询配置自定义映射关系
//查询所有班级
    @Select("select * from classes")
    @Results(id="classesMapper",value = {
            @Result(id = true,property ="cid",column = "cid"),
            @Result(property = "className",column = "className"),
            //many:表示该属性是一个集合
            @Result(property = "studentList",column = "sid",
            many = @Many(select = "com.xyy.mapper.StudentMappper.findByClassesId",fetchType = FetchType.LAZY))
    })
    List<Classes> findAll();
  1. 测试
 @Test
    public void findAllClasses(){
        classesMapper classesMapper1=session.getMapper(classesMapper.class);
        List<Classes> all=classesMapper1.findAll();
        all.forEach(System.out::println);
    }

注解开发与映射文件开发的对比

MyBatis中更推荐使用映射文件开发,Spring、SpringBoot更推荐
注解方式。具体使用要视项目情况而定。它们的优点对比如下:

映射文件:

  • 代码与Sql语句是解耦的,修改时只需修改配置文件,无需修改源码。
  • Sql语句集中,利于快速了解和维护项目。
  • 级联查询支持连接查询和分解查询两种方式,注解开发只支持分解查询。

注解:

  • 配置简单,开发效率高。
  • 类型安全,在编译期即可进行校验,不用等到运行时才发现错误。

PageHelper分页插件

在这里插入图片描述
开发过程中如果要进行分页查询需要传入页数和每页条数。返回页面数据,总条数,总页数,当前页面,每页条数等数据。此时使用PageHelper插件可以快速帮助我们获取这些数据。

PageHelper是一款非常好用的开源免费的Mybatis第三方分页插件。使用该插件时,只要传入分页参数,即可自动生成页面对象。使用该插件分页查询所有用户:

1.引入依赖

<!-- 分页插件pagehelper -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.0.0</version>
</dependency>
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-autoconfigure</artifactId>
    <version>1.2.3</version>
</dependency>
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.3</version>
</dependency>
<!-- 分页插件pagehelper -->

2.Mybatis配置文件中配置PageHelper插件

<plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <property name="helperDialect" value="mysql"/>
        </plugin>
    </plugins>
  1. 使用PageHelper插件
 @Test
    public void testFindPage(){
        //(1)查询前设置分页参数,参数一:页数,从1开始。参数二:每页条数
        PageHelper.startPage(1,3);
        //正常查询
        List<User> all=userMapper.findAll();
        //创建页面对象,创建时将查询结果传入构造方法
        PageInfo pageInfo=new PageInfo(all);
        //打印页面对象的属性
        System.out.println("结果集:"+pageInfo.getList());
        System.out.println("总条数:"+pageInfo.getTotal());
        System.out.println("总页数:"+pageInfo.getPages());
        System.out.println("当前页:"+pageInfo.getPageNum());
        System.out.println("每页条数:"+pageInfo.getSize());


    }

在这里插入图片描述

标签:映射,List,查询,user,MyBatis,public
From: https://blog.csdn.net/m0_73852099/article/details/141686946

相关文章

  • MyBatis-Plus保姆级快速上手教程
    为简化开发而生Mybatis简化JDBC操作MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。1、特性无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑损耗小:启动即会自动注入基本CURD,性能基本无损耗,直接面向对......
  • MyBatis如何自定义项目中SQL日志
    说明:用过MyBatis框架的同学们都知道,打印SQL日志,可以通过在application.yml配置文件中加入下面配置来设置:mybatis:configuration:log-impl:org.apache.ibatis.logging.stdout.StdOutImpl但打印出来的SQL如下,丑陋不堪,不够优雅本文介绍如何自定义SQL日志拦截器......
  • MyBatis错误(一)
    说明:记录一次MyBatis错误,错误信息如下,说数字转换异常,显然,把一个字符串类型转为数字类型,肯定是不行的。2024-08-2919:44:43.198ERROR24216---[nio-9090-exec-2]o.a.c.c.C.[.[.[/].[dispatcherServlet]:Servlet.service()forservlet[dispatcherServlet]incon......
  • PostgreSQL -- 使用 Mybatis 时对数据库的多个删除操作
     在Java中使用Mybatis与PostgreSQL数据库进行交互时,删除操作的语句根据不同的场景应用不同。 1.删除表内所有记录删除表内所有的记录。谨慎使用!<!--1.删除表内所有记录--><deleteid="deleteALl">deletefromtable_name;</delete>2......
  • springboot+vue+mybatis计算机毕业设计飞机订票系统+PPT+论文+讲解+售后
    快速发展的社会中,人们的生活水平都在提高,生活节奏也在逐渐加快。为了节省时间和提高工作效率,越来越多的人选择利用互联网进行线上打理各种事务,然后线上管理系统也就相继涌现。与此同时,人们开始接受方便的生活方式。他们不仅希望页面简单大方,还希望操作方便,可以快速锁定他们需......
  • MyBatis中的#和$:深入解析与实战应用
    MyBatis中的#和$:深入解析与实战应用作为一名编程博客专家,我将带领大家深入探讨MyBatis中#和$的区别,并通过详细的代码示例和解释,帮助大家全面理解其工作原理及实际应用。前置知识在深入探讨之前,我们需要了解一些基本概念:MyBatis:一个持久层框架,支持自定义SQL、存储过程以......
  • springboot+vue+mybatis计算机毕业设计电影影评的垂直搜索引擎+PPT+论文+讲解+售后
    近年来,科技飞速发展,在经济全球化的背景之下,大数据将进一步提高社会综合发展的效率和速度,大数据技术也会涉及到各个领域,而爬虫实现网站数据可视化在网站数据可视化背景下有着无法忽视的作用。管理信息系统的开发是一个不断优化的过程,随着网络大数据时代的到来,管理信息系统与大......
  • springboot+vue+mybatis计算机毕业设计房屋租赁管理系统+PPT+论文+讲解+售后
    随着社会的不断进步与发展,人们经济水平也不断的提高,于是对各行各业需求也越来越高。特别是从2019年新型冠状病毒爆发以来,利用计算机网络来处理各行业事务这一概念更深入人心,由于工作繁忙以及疫情的原因,房屋租赁也是比较难实施的。如果开发一款房屋租赁管理系统,可以让用户在最......
  • MyBatis-Plus的使用
    MyBatis-PlusMyBatis-Plus1.MyBatis-Plus原理概述2.动手实践**2.1环境搭建****2.2实体类与Mapper接口****2.3服务层实现****2.4控制器层**3.高级功能与优化3.1复杂查询条件构造:Wrapper类3.2逻辑删除3.3字段填充3.4分页和排序3.5.全局拦截器MyBatis-P......
  • 【阅己书城】整合mybatis-plus
    1、导入依赖<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.2.0</version></dependency>2、配置#导入驱......