mybatis

1.mybatis

是一个持久层框架,通过XML配置或者注解配置,省去了所有的JDBC代码(除了sql语句),比如获取连接,设置参数,返回结果集。

开源在githup上

1.1mybatis使用

  1. maven仓库

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.6</version>
    </dependency>
    
    
  2. GitHub

    Releases · mybatis/mybatis-3 · GitHub

  3. 中文文档

    MyBatis中文网

1.2 为什么使用mybatis

自动化配置,实现sql和java代码分离,提高维护性。

2.第一个mybatis程序

搭建环境–》导入mybatis–》编写代码–》测试

2.1 搭建环境

  1. 创建maven项目

  2. 删除src目录,就可以当成maven的父目录,父目录中的依赖可以不用导入

  3. 导入maven依赖

    <!--导入依赖-->
        <dependencies>
    
    <!--        mybatis-->
            <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.2</version>
            </dependency>
    <!--        mysql-->
            <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.12</version>
            </dependency>
    <!--        junit-->
            <!-- https://mvnrepository.com/artifact/junit/junit -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
    
        </dependencies>
    
    • dependencies使用

    2.2创建子模块

    1. resources目录下配置mybatis核心配置文件,连接数据库
    <?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.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis_study?useSSL=false&amp;useUnicode=true&amp;serverTimezone=UTC&amp;characterEncoding=utf8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
        <mappers>
            <mapper resource="dao/UserMapper.xml"/>
        </mappers>
    </configuration>
    
<mappers>
        <mapper resource="dao/UserMapper.xml"/>
    </mappers>

不要忘记mapper.xml的注入会报错

org.apache.ibatis.binding.BindingException: Type interface com.kuang.dao.UserDao is not known to the MapperRegistry.
<?xml version="1.0" encoding="UTF-8" ?>
//中文问题,mysql数据库中需要用utf8,将utf-8改为utf8
<?xml version="1.0" encoding="UTF8" ?>

2.编写mybatis工具类,获取sqlSession相当于connection

public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            //获取sqlSessionFactory,固定代码
            String resource = "mybatis_config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //通过sqlSessionFactory获取sqlSession
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();

    }
}

3.编写代码

实体类

public class User {
    private int id;
    private String name;
    private String pwd;

    public User() {
    }

    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}

2.接口类

public interface UserDao {
    List<User> getUsers();
}

3.接口实现类

<?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="dao.UserDao">
    <select id="getUsers" resultType="pojo.User">
        select * from users where id = 1;
    </select>
</mapper>

4.测试

public class UserDaoTest {
    @Test
    public void test(){
        //获取sqlSession
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //获取接口UserDao,调用方法
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        List<User> users = mapper.getUsers();
        //输出
        for(User user:users){
            System.out.println(user);
        }
    }
}

细节

*static静态代码块其实完全可以看做是一个没有名字、没有参数、没有返回值的静态方法*

SqlSession sqlSession = sqlSessionFactory.openSession();
        return sqlSession;

代码优化

return sqlSessionFactory.openSession();

常见错误

  1. sql查询语句标签名

  2. jdbc数据库连接url

  3. 编码,mapper.xml和mybatis-config.xml中中文乱码问题

  4. mybatis中mapper.xml注册资源路径

  5. mapper.xml文件导出的问题,maven中pom.xml中需要扫描导出

        <!--在build中配置resources,来防止我们资源导出失败的问题-->
        <build>
            <resources>
                <resource>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>true</filtering>
                </resource>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>true</filtering>
                </resource>
            </resources>
        </build>
    

3.CURD

只需要修改接口,接口实现类,测试类

1.实现接口,UserMapper

public interface UserMapper {
    List<User> list();
    User getUserById(int id);
    int addUser(User user);
    int updateUser(User user);
    int deleteUser(int id);
}
  • 参数
  • 返回值
  • 方法名规范

2.接口实现类 mapper.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="com.qiang.dao.UserMapper">
    <select id="list" resultType="com.qiang.pojo.User">
        select * from users
    </select>
    <select id="getUserById" resultType="com.qiang.pojo.User" parameterType="int">
        select * from users where id=#{id}
    </select>
    <insert id="addUser" parameterType="com.qiang.pojo.User">
        insert into users (`id`,`name`,`pwd`) values (#{id},#{name},#{pwd})
    </insert>
    <update id="updateUser" parameterType="com.qiang.pojo.User">
        update users set `name`=#{name},`pwd`=#{pwd} where id=#{id}
    </update>
    <delete id="deleteUser" parameterType="com.qiang.pojo.User">
        delete from users where id=#{id}
    </delete>
</mapper>
  • nameplace 空间命名对应接口,相当于实现接口
  • id对应方法名,相当于实现方法
  • 返回类型和参数
  • {id}可以获取对象中的属性,且要和对象属性一一对应
  • 表名前加数据库名,idea连接数据库后,没连不用写,直接写表名

3.测试

import com.qiang.dao.UserMapper;
import com.qiang.pojo.User;
import com.qiang.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

import java.util.List;

public class test {
    @Test
    public void test(){
        //获取sqlSession
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //获取接口UserDao,调用方法
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.list();
        User user1 = mapper.getUserById(1);
        //输出
        for(User user:users){
            System.out.println(user);
        }
        System.out.println(user1);
        sqlSession.close();
    }
    @Test
    public void addUserTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int i = mapper.addUser(new User(4,"lixin","123456"));
        if(i>0){
            System.out.println("插入成功!");
        }
        sqlSession.commit();
        sqlSession.close();
    }
    @Test
    public void updateUserTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int i = mapper.updateUser(new User(4, "程咬金", "123456"));
        if(i>0){
            System.out.println("更改成功!");
        }
        sqlSession.commit();
        sqlSession.close();
    }
    @Test
    public void deleteUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int i = mapper.deleteUser(3);
        if(i>0){
            System.out.println("删除成功!");
        }
        sqlSession.commit();
        sqlSession.close();
    }
}

  • 增删改要提交事务
  • 记得释放资源sqlSession
  • @Test
  • 传值 new User(4,“fewhi”,“5113”) 双引号

map

修改某一字段,如果字段过多,使用User传值,需要写出全部的字段,使用map最多传个id,随意简便。

使用User

mapper.updateUser1(new User(1,"王昭君","123456"));

使用map 键值对 key

int updateUser(Map<String,Object> map);
<update id="updateUser" parameterType="Map">
    update users set `name`=#{onename} where id=#{oneid}
</update>
 @Test
    public void updateTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        HashMap<String, Object> map = new HashMap<>();
        map.put("oneid", 1);
        map.put("onename", "宋尚香");
        mapper.updateUser(map);
        sqlSession.commit();
        sqlSession.close();
    }
  • 简便,省去过多字段
  • 不用像User一样,需要一一对应属性名,可以自定义

map传参数,sql中用key 【 parameterType=“Map”】

对象传参数,sql中用属性 【parameterType=“pojo.User”】

只用一个基本属性,比如int 【parameterType=“int”】省略不写

多个属性使用map或者注解

模糊查询

List<User> getUserLike(String value);
select * from users where `name` like #{value}   
List<User> users = mapper.getUserLike("李%");
  • 不安全,用户传入id为1&1=1,字符串拼接,存在注入问题

解决:可以把值固定,只能传value值

select * from users where `name` like "%"#{value}"%"
List<User> users = mapper.getUserLike("李");

4.配置解析

不是是会简单的curd,学会配置,学会看官方文档

4.1 配置环境

可以配置多种环境,但是一个实例中只能选择一种环境,要通过default指定要使用的环境

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--配置文件-->
<configuration>
<!--    引入外部配置文件-->
    <properties resource="db.properties"/>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>

4.2 配置文件

1.在resources文件中写db.properties文件

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis_study?useSSL=false&useEncoding=true&characterEncoding=utf8&serverTimezone=UTC
username=root
password=123456

2.引入配置文件

//第一种方式 自闭合
<properties resource="db.properties"/>
//第二种方式 可以设置属性 优先级低于properties中
<properties resource="db.properties">
  <property name="username" value="name"/>
  <property name="password" value="123456"/>
</properties>

注意位置,应最前面,xml中的配置有顺序

properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?

image-20211103193933198

别名

解决mapper.xml配置文件中类名复杂的问题

1.指定具体,适用于别名较少的

<!--   可以起别名 -->
    <typeAliases>
        <typeAlias type="com.qiang.pojo.User" alias="User"></typeAlias>
    </typeAliases>

2.通过扫描包下所有的JavaBean类,默认为类名小写,首字母大写好像也行,适用于类多的情况

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

3.注解,可以在类中通过注解指定别名

@Alias("hello")

4.3 设置

image-20211103203846890

4.4 映射器

注册绑定mapper文件

1.使用resources

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

2.使用class类名

<mappers>
        <mapper class="com.qiang.dao.UserMapper"/>
    </mappers>
  • 类名和配置名要相同,且在同一包下
  • image-20211103205223750

3.使用package

<mappers>
        <package name="com.qiang.dao"/>
    </mappers>

4.5 生命周期和作用域

在这里插入图片描述

错误的使用作用域和生命周期,会引发并发问题

SqlSessionFactoryBuild

  • 创建SQLSessionFactory,创建玩就不需要了

  • 局部变量

  • sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    

SqlSessionFactory

  • 理解为数据库连接池

  • 创建sqlSession,需要一直存在,应用启动就一直存在

  • 全局变量,应用作用域

  • private static SqlSessionFactory sqlSessionFactory;
    return sqlSessionFactory.openSession();
    

sqlSession

  • 理解为一个请求

  • 调用方法,接口,实现业务

  • 在方法体类

  • public void getUsers(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<User> users = mapper.getUsers();
            for(User user :users ){
                System.out.println(user);
            }
    

image-20211104083013554

每一个mapper相当于业务。实现SQL语句

5.ResultMap结果集

解决;属性名和字段名不一致问题,查询不到数据

image-20211104085337766image-20211104085349500

image-20211104085424473

解决方法一:给字段起别名

select `id`,`name`,`pwd` as `password` from users

解决方法二:resultMap结果集映射

<!--    将结果集UserMap映射为User类 id 是引用 type映射的类-->
    <resultMap id="UserMap" type="User">
<!--        property 类中的属性 column 数据库的字段-->
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="password" column="pwd"/>
    </resultMap>

resultMap结果集

  • 简单语句可以做到0配置,复杂语句只要说明关系

6.日志

6.1 日志工厂

解决SQL语句报错,方便找到bug

配置日志

<!--    设置-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
  • 类名和值要严格,大小写,不能有空格
  • 默认为STDOUT_LOGGING 适用其他日志需要导入包

image-20211104103800898

6.2 log4j

定义

log4j是Apache下的一个开源项目,可以通过修改配置文件,控制日志文件的输出方式,控制台,文件,和控制日志的格式,同时能通过Logger对象来定义日志信息的级别,输出,debug,error,更加清除生成过程

1.导入log4j包

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

2.log4j配置文件

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/qiang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

3.设置log4j

<!--    设置-->
    <settings>
<!--        log4j-->
        <setting name="logImpl" value="LOG4J"/>
    </settings>

4.使用

image-20211104111558979

使用

尽量只用log4j,配置文件可以想自定义修改,可以在log文件中通过前缀看到信息方式

1.在需要使用log4j的类中导入import org.apache.log4j.Logger;,一定是apache下的

2.日志对象,参数为当前类中的class

static Logger logger = Logger.getLogger(UserMapperTest.class);

3.实现级别

logger.info("输出");
logger.debug("debug");
logger.error("错误");

7.分页

减少数据库的压力,提高用户体验

7.1 使用limit分页

SELECT * FROM users LIMIT 0,2

本质是通过mybaties对SQL语句的实现

1.Usermapper

//分页
    List<User> getUserByLimit(Map<String,Integer> map);

2.UserMapper.xml

<select id="getUserByLimit" resultMap="UserMap" parameterType="map">
select * from users limit #{startIndex},#{pageSize}
</select>

3.test

    @Test
    public void getUserByLimitTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        HashMap<String, Integer> map = new HashMap<>();
        map.put("startIndex", 0);
        map.put("pageSize", 2);
        List<User> users = mapper.getUserByLimit(map);
        for(User user:users){
            System.out.println(user);
        }
    }
  • 通过map实现,Map的别名

image-20211104161757934

7.2 RowBounds

1.接口

List<User> getUsersByRowBounds();

2.接口实现类

    <select id="limit" resultType="pojo.User" parameterType="map">
        select * from users limit #{startIndex},#{pageSize}
    </select>

3.test

    @Test
    public void getUsersByRowBoundsTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        RowBounds rowBounds = new RowBounds(0, 2);
        List<User> users = sqlSession.selectList("com.qiang.dao.UserMapper.getUsersByRowBounds",null,rowBounds);
        for(User user :users){
            System.out.println(user);
        }
        sqlSession.close();
    }
  • List<User>List<Object>
  • sqlSession.close();不要忘记,最好一开始就写好

7.3 分页插件

image-20211105084626095

分页的实现本质上是通过limit

1.sql语句加map传参的方法

2.RowBounds实现,通过直接调用方法,全限定名实现,不推荐使用,快被淘汰

8.使用注解开发

8.1 面向接口

面向接口编程:解耦,每一个接口相当于程序的骨架,只需要实现每个接口就行,注解的使用符合面向接口编程

mybatis中可以在接口上使用注解,可以简化开发,省写mapper.xml文件,但是只支持简单的sql,复杂的sql不行,比如字段和属性不匹配的话,使用注解查询不到,所以简单的可以使用注解开发,但还是推荐使用xml

底层原理:反射,通过UserMapper mapper = sqlSession.getMapper(UserMapper.class);反射获取接口的一切东西,接口的SQL实现,和方法自然就可以实现了

1.接口加注解 只要写接口,不用xml

public interface UserMapper {
    @Select("select * from users")
    List<User> getUsers();
}

2.配置文件mybatis-config.xml中注册mapper接口

    <mappers>
        <mapper class="com.qiang.dao.UserMapper"/>
    </mappers>

3.测试

public class UserMapperTest {
    @Test
    public void getUsersTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.getUsers();
        for(User user:users){
            System.out.println(user);
        }
        sqlSession.close();
    }
}

8.2.mybatis执行流程

在这里插入图片描述

通过debug分析

8.3 CRUD注解开发

开启自动提交

return sqlSessionFactory.openSession(true);

可以省略sqlSession.commit;但不建议使用,会错误提交信息

CRUD注解开发

接口

public interface UserMapper {
    @Select("select * from users")
    List<User> getUsers();
    //多个参数时,一定要加上@Param指定sql语句#{id}获取的值是,引用对象user不需要写
    @Select("select * from users where id = #{id}")
    User getUserById(@Param("id") int id);
    @Insert("insert into users (`id`,`name`,`pwd`) values(#{id},#{name},#{password})")
    int add(User user);
    @Update("update users set `name`=#{name},`pwd`=#{password} where `id`=#{id}")
    int update(User user);
    @Delete("delete from users where id=#{id}")
    int delete(@Param("id") int id);
}
  • @Param的使用,有多个基本参数时,一定要使用@Param,一个参数可以不用,引用参数可以不用比如Userdeng
  • @Param(“id1”)对应的sql中的#{id1},是一一对应的

注意在配置文件中绑定注册

    <mappers>
        <mapper class="com.qiang.dao.UserMapper"/>
    </mappers>

8.4Lombok

简化操作,工具

@Data //无参,getset方法,tostring
@AllArgsConstructor //有参,写了后没有无参需要再导入
@NoArgsConstructor //无参
public class User {
    private int id;
    private String name;
    private String password;
}
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
    <scope>provided</scope>
</dependency>

9.多对一处理

多个学生关联一个老师

9.1 嵌套子查询处理

1.先查询所有学生

2.根据tid查老师,相当于子查询,先查出学生有哪些,然后根据s.tid=t.id,查老师

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qiang.dao.StudentMapper">

    <select id="getStudents" resultMap="StudentTeacher">
        select * from student
    </select>
    <resultMap id="StudentTeacher" type="student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    <select id="getTeacher" resultType="Teacher">
        select * from teacher where id=#{id}
    </select>
</mapper>
  • 用resultMap解决属性teacher和字段tid不对应的问题,但是返回的还是Student类
  • 遇到复杂属性需要单独处理,对象用association,集合用collection

9.2 结果集查询处理

相当于连表查询,只要对应上字段即可

<!--    结果集查询-->
    <select id="getStudents2" resultMap="StudentTeacher1">
        select s.id sid,s.name sname,t.name tname
        from student s,teacher t
        where s.tid=t.id
    </select>
    <resultMap id="StudentTeacher1" type="student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"/>
        </association>
    </resultMap>

10.一对多处理

一个老师关联多个学生

10.1嵌套子查询

<!--    子查询-->
    <select id="getTeacher2" resultMap="TeacherStudent1">
        select * from teacher where id=#{tid}
    </select>
    <resultMap id="TeacherStudent1" type="teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <collection property="students" column="id" ofType="Student" select="getStudent" javaType="ArrayList"></collection>
    </resultMap>
    <select id="getStudents" resultType="student">
        select `id`,`name`,`tid` from student where tid=#{tid}
    </select>
  • 两个条件

10.2 结果集查询

<!--    结果集-->
    <select id="getTeacher" resultMap="TeacherStudent">
        select s.id sid,s.name sname,t.name tname,t.id tid
        from student s,teacher t
        where s.tid=t.id and t.id = #{tid}
    </select>
    <resultMap id="TeacherStudent" type="teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <collection property="students" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
        </collection>
    </resultMap>
  • 按照结果进行映射

总结

  1. association对象 collection集合
  2. resultMap 实体中属性对应的类型 比如ArrayList,oftype 映射到集合或者List的pojo类型比如User
  3. 注意不同表和实体的属性和字段的对应

11动态sql

11.1自动生成id

//自动生成id
public class IdUtils {
    public static String getId(){
        return UUID.randomUUID().toString().replaceAll("-", "");
    }
    @Test
    public void test(){
        System.out.println(IdUtils.getId());
        System.out.println(IdUtils.getId());
        System.out.println(IdUtils.getId());
        System.out.println(IdUtils.getId());
    }
}

11.2动态sql

if

List<Blog> queryBlog(Map map);

<mapper namespace="com.qiang.dao.BlogMapper">
   <select id="queryBlog" parameterType="map" resultType="Blog">
        select * from blog
        <where>
<!--        <if test="title != null">-->
<!--            and title=#{title}-->
<!--        </if>-->
        <if test="author != null">
            and author=#{author}
        </if>
        </where>
    </select>
</mapper>

public class BlogTest {
    @Test
    public void queryBlogTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap<String, Object> map = new HashMap<>();
        map.put("title", "python");
        map.put("author", "后羿");
        List<Blog> blogs = mapper.queryBlog(map);
        for(Blog blog:blogs){
            System.out.println(blog);
        }
    }
}

choose

    <select id="queryBlogChoose" parameterType="map" resultType="Blog">
        select * from blog
    <where>
        <choose>
            <when test="title!=null">
                title=#{title}
            </when>
            <when test="author!=null">
                author=#{author}
            </when>
            <otherwise>
                view=#{view}
            </otherwise>
        </choose>
    </where>
    </select>
  • 只会满足一个条件,后面的即使满足也不会执行,相当于Switch case

set

   <update id="updateBlog" parameterType="map">
        update blog
        <set>
            <if test="title!=null">
                title=#{title},
            </if>
            <if test="author!=null">
                author=#{author}
            </if>
        </set>
        where id=#{id}
    </update>
  • set标签会自动加set关键词,去掉多余的逗号

动态SQL本质上还是sql,就是通过if判断是否为空,去增加sql条件,在SQL层面增加逻辑

sql片段

抽取重复片段,实现代码复用

   <sql id="updateSql">
        <set>
            <if test="title!=null">
                title=#{title},
            </if>
            <if test="author!=null">
                author=#{author}
            </if>
        </set>
    </sql>
        <update id="updateBlog" parameterType="map">
        update blog
        <include refid="updateSql"></include>
        where id=#{id}
    </update>
  • sql标签抽取,include标签进行引用
  • 尽量在单表中使用,不要包含where和set标签
        for(Blog blog:blogs){
            System.out.println(blog);
        }
  • 可以通过blog.for实现

forEach

实现动态SQLselect * from blog where 1=1 and (id=1 or id=2 or id=3)

List<Blog> getBlogsForEach(Map map);

<!--    select * from blog where 1=1 and (id=1 or id=2 or id=3)-->
    <select id="getBlogsForEach" parameterType="map" resultType="Blog">
        select * from blog
        <where>
            <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                id=#{id}
            </foreach>
        </where>
    </select>
    
@Test
    public void getBlogsForEach(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap<String, Object> map = new HashMap<>();
        ArrayList<Object> ids = new ArrayList<>();
        ids.add(1);
        ids.add(2);
        map.put("ids",ids);
        List<Blog> blogs = mapper.getBlogsForEach(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }

        sqlSession.close();
    }
}
  • 在mysql中实现SQL保证SQL的正确性,再用动态SQL实现,本质上还是sql语句

  • 通过map,List给sql传值

  • 元素主要用在构建 in 条件中,它可以在 SQL 语句中迭代一个集合。

    元素的属性主要有 item、index、collection、open、separator、close。

    • item 表示集合中每一个元素进行迭代时的别名。
    • index 指定一个名字,用于表示在迭代过程中每次迭代到的位置。
    • open 表示该语句以什么开始。
    • separator 表示在每次进行迭代之间以什么符号作为分隔符。
    • close 表示以什么结束。

    在使用 元素时,最关键、最容易出错的是 collection 属性,该属性是必选的,但在不同情况下该属性的值是不一样的,主要有以下 3 种情况:

    • 如果传入的是单参数且参数类型是一个 List,collection 属性值为 list。
    • 如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array。
    • 如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。

    实例

    select * from blog where id in (1,2,3)

List<Blog> getBlogsForEachIn(List<Integer> list);

<!--    select * from blog where id in (1,2,3)-->
    <select id="getBlogsForEachIn" resultType="blog" parameterType="List">
        select * from blog
        <where>
            <foreach collection="list" open="id in (" close=")" separator="," item="id">
             #{id}
            </foreach>
        </where>
    </select>
    
        @Test
    public void getBlogsForEachIn(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        ArrayList<Integer> ids = new ArrayList<>();
        ids.add(1);
        ids.add(2);
//        ids.add(3);
        List<Blog> blogs = mapper.getBlogsForEachIn(ids);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        sqlSession.close();
    }
}
  • 也可以直接在写成select * from blog where id in 后面接(1,2,3)

12 缓存

在内存中对数据进行备份,当数据没有改变的情况下,可以 直接在缓存中取数据,而不用去数据库读数据,减少读写次数,提高效率。

主要应用于查询量大的操作

常见的数据库缓存技术

这里写图片描述

12.1 一级缓存

mybatis会默认开启,sqlSession级别的,会在sqlSession创建和清除间隔中存在,相当于一个map

缓存失效

1.执行一个新的查询

2.增删改

3.不同的mapper.xml

4.手动关闭缓存

sqlSession.clearCache();

没有太多用处,只有重复查询一个东西时有用

12.2 二级缓存

在同一个Mapper中有效,是nameSpace级别的缓存,数据会暂时保存在一级缓存中,但是sqlSession提交或者关闭,就会把数据存到二级缓存中。

开启

在mapper.xml中可以通过cache标签开启

1.mybatis-config.xml中开启全局缓存

<!--        开启全局缓存-->
        <setting name="cacheEnabled" value="true"/>

2.在要使用到的mappe.xmlr中开启

  1. 不定义参数
<cache/>
  • 会出现序列化bug
Error serializing object.  Cause: java.io.NotSerializableException: com.qiang.pojo.User
  • 解决

  • 加参数readOnly="true" 或者将实体类序列化 类后面加public class User implements Serializable  
    

2.自定义参数

<cache readOnly="true" eviction="FIFO" flushInterval="60000" size="512"/>
  • 二级缓存是只有sqlSession关闭或者提交,才将数据存入二级缓存,所以是针对整个mapper来说的

  • 通过查看控制台输出,没有重新执行SQL,就直接取,就是缓存生效了

image-20211108170727444

12.3 缓存原理

image-20211108171306420

  • 先查二级缓存,再查一级缓存 最后查数据执行sql
  • 一级缓存是针对sqlSession来说的,SQLSession没有关闭,数据还在一级缓存中,所以先查二级缓存再查一级缓存

12.4 自定义缓存ehcache

,可以 直接在缓存中取数据,而不用去数据库读数据,减少读写次数,提高效率。

主要应用于查询量大的操作

常见的数据库缓存技术

这里写图片描述

12.1 一级缓存

mybatis会默认开启,sqlSession级别的,会在sqlSession创建和清除间隔中存在,相当于一个map

缓存失效

1.执行一个新的查询

2.增删改

3.不同的mapper.xml

4.手动关闭缓存

sqlSession.clearCache();

没有太多用处,只有重复查询一个东西时有用

12.2 二级缓存

在同一个Mapper中有效,是nameSpace级别的缓存,数据会暂时保存在一级缓存中,但是sqlSession提交或者关闭,就会把数据存到二级缓存中。

开启

在mapper.xml中可以通过cache标签开启

1.mybatis-config.xml中开启全局缓存

<!--        开启全局缓存-->
        <setting name="cacheEnabled" value="true"/>

2.在要使用到的mappe.xmlr中开启

  1. 不定义参数
<cache/>
  • 会出现序列化bug
Error serializing object.  Cause: java.io.NotSerializableException: com.qiang.pojo.User
  • 解决

  • 加参数readOnly="true" 或者将实体类序列化 类后面加public class User implements Serializable  
    

2.自定义参数

<cache readOnly="true" eviction="FIFO" flushInterval="60000" size="512"/>
  • 二级缓存是只有sqlSession关闭或者提交,才将数据存入二级缓存,所以是针对整个mapper来说的

  • 通过查看控制台输出,没有重新执行SQL,就直接取,就是缓存生效了

[外链图片转存中…(img-sYilveRb-1636376570322)]

12.3 缓存原理

[外链图片转存中…(img-1M4fkprQ-1636376570325)]

  • 先查二级缓存,再查一级缓存 最后查数据执行sql
  • 一级缓存是针对sqlSession来说的,SQLSession没有关闭,数据还在一级缓存中,所以先查二级缓存再查一级缓存

12.4 自定义缓存ehcache

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值