mybaties学习笔记

本文深入介绍了 MyBatis 框架的使用,包括持久层、Spring 整合、Shiro 安全框架、Git 版本控制,以及 JUnit 单元测试。详细讲解了 MyBatis 的核心配置文件、Mapper 接口、XML 映射文件、日志配置、动态 SQL 和缓存机制。还提供了 CRUD 操作、多对一和一对多关系映射的示例,并探讨了如何处理字段名与属性名不一致的问题。最后,讨论了 MyBatis 中的 SQL 片段和缓存功能,以及如何自定义缓存策略。

mybatis框架 持久层

Spring

Spring MVC 表示层

shiro 安全框架

git 版本控制工具

项目阶段

Junit

使用Junit做单元测试的目的:

  • 方便运行代码,人为查看结果符不符合预期

  • 使用断言测试让机器去判断结果符不符合预期,a开头的很多junit断言测试

    User one = UserDao.getOne(1);
    asserEquals(null,one);

@Test注释的方法成为测试用例

测试注解使用的要求:

  • 要注解在某个方法上

  • 该方法要求无参,返回值为空,且为public

在test里面添加参数

@Test(timeout=1000)

限时测试:timeout属性指定毫秒值,要求该单元测试用例在规定时间内完成

@Before

作用于某个方法上

作用:所有的测试单元执行之前执行该方法,每执行一个测试单元就会执行一次Before

要求:

  • 要注解在某个方法上

  • 该方法要求无参,返回值为空,且为public

@After

作用于某个方法上

作用:在所有单元测试用例之后执行该方法(多用于资源回收)

要求:

  • 要注解在某个方法上

  • 该方法要求无参,返回值为空,且为public

@BeforeClass

在整个测试类的所有测试用例运行之前运行一次

  • 要注解在某个方法上

  • 该方法要求无参,返回值为空,还要被public static修饰!!

@AfterClass

在整个测试类的所有测试用例运行完之后运行一次

  • 要注解在某个方法上

  • 该方法要求无参,返回值为空,还要被public static修饰!!

@Ignore

忽略这个测试用例,但是会在总计结果中显示统计信息

@Runwith()

套件测试,同时测试多个测试类。所谓Runner提供单元你测试运行的环境,默认为Junit4.class

@RunWith(Suite.class)
@Suite.SuiteClasses(UserDaoImplTest.class,AppTest.class)

@Suite.SuiteClasses指定该套件中包含了哪些测试类

1.简介

  • MyBatis 是一款优秀的持久层框架

  • 它支持自定义 SQL、存储过程以及高级映射。

  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

  • MyBatis 可以通过简单的 XML注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

 

1.1.获取mybaties

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

1.2.持久层

Dao层(数据库操作层),Service层(业务层),Control层(专门接收用户请求层)。

  • 持久层:完成持久化工作的代码块

  • 层是界限十分明显的

持久化是一个动作,持久层是一个概念。

1.3.为什么需要Mybatis

  • 方便

  • 传统的JDBC太复杂,简化,框架,自动化

  • 帮助程序员将数据存入到数据库中

  • 不用mybatis也可以,更容易上手

  • 用的人特别多

spring,spring mvc,springboot

2.第一个mybatis程序

步骤:搭建环境-》导入mybatis-》编写代码-》测试

搭建数据库环境

2.1导入xml依赖

    <dependencies>
<!--        mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
<!--        mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>
<!--        测试工具junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
​
    </dependencies>

2.2.创建一个maven子项目

  • 配置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核心配置文件-->
    <configuration>
    <!--    environments配置多个环境-->
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
    <!--                在xml中&符号需要进行转义&amp-->
                    <property name="url" value="jdbc:mysql:///mybatis"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
    </configuration>

2.3编写mybatis工具类

//第一步从 XML 中构建 SqlSessionFactory
public class MybatisUtil {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            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();
    }
}
​

2.4编写代码

  • 实体类

public class User {
    private int id;
    private String name;
    private String password;
​
    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 getPassword() {
        return password;
    }
​
    public void setPassword(String password) {
        this.password = password;
    }
​
    public User() {
    }
​
    public User(int id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }
​
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

  • Dao接口

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

  • 接口实现类

<?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">
<!--namespace命名空间绑定一个Maper接口-->
<mapper namespace="com.liu.Dao.UserDao">
<!--    id对应UserDao的方法,resultType对应实体类-->
   <select id="getUserList" resultType="com.liu.Pojo.User">
       select * from mybatis.user
   </select>
</mapper>

 

2.5测试

注意:

org.apache.ibatis.binding.BindingException: Type interface com.liu.Dao.UserMaper is not known to the MapperRegistry.

MapperRegistry是什么?

核心配置文件中注册mapers

  • junit测试

public class UserDaoTest {
    @Test
    public void test() {
        //第一步获取sqlSession对象
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        //执行SQL
        //方式一getMapper
        UserMaper mapper = sqlSession.getMapper(UserMaper.class);
        List<User> userList = mapper.getUserList();
​
        for (User user : userList) {
            System.out.println(user);
        }
        //关闭sqlSession
        sqlSession.close();
    }
}

可能遇到的问题:

1.配置文件没有注册

2.绑定接口错误

3.方法名不对

4.返回类型不对

5.Maven导出资源问题

3.CRUD

1.namespace

namespace中的包名要和Mapper接口的报名一致

2.select

选择,查询语句;

  • id:就是对应namespace中的方法名

  • resulType:sql语句执行的返回值!

  • paramterType:参数类型!

1.编写接口

2.编写对应的mapper中的sql

3.测试

3.Insert

UserMapper:

  //插入一条记录
    public void insertOne(User user);

UserMapper.xml

<insert id="insertOne" parameterType="com.liu.Pojo.User">
        insert into user values (#{id},#{name},#{password})
    </insert>

Test:

 //插入用户
    @Test
    public void insertOne(){
        SqlSession sqlSession = MybatisUtil.getsqlSession();
​
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.insertOne(new User(4,"liulang","123456"));
​
        //提交事务
        sqlSession.commit();
        sqlSession.close();
    }

4.Update

UserMapper:

    //修改记录
    public int alterUser(User user);

UserMapper.xml

 <update id="alterUser" parameterType="com.liu.Pojo.User">
        update user set name=#{name},password=#{password} where id=#{id};
    </update>

Test:

  //修改信息
    @Test
    public void alterUser(){
        SqlSession sqlSession = MybatisUtil.getsqlSession();
​
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int i = mapper.alterUser(new User(4, "liulang", "123"));
        if (i>0)
            System.out.println("修改成功");
        else
            System.out.println("修改失败");
​
        //提交事务
        sqlSession.commit();
        sqlSession.close();
    }

5.Delete

UserMapper:

//删除用户
    public int deleteUser(int id);

UserMapper.xml

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

Test:

   //删除用户
    @Test
    public void deleteUser(){
        SqlSession sqlSession = MybatisUtil.getsqlSession();
​
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int i = mapper.deleteUser(4);
        if (i>0)
            System.out.println("删除成功");
        else
            System.out.println("删除失败");
​
        //提交事务
        sqlSession.commit();
        sqlSession.close();
    }

注意:增删改都需要提交事务才会生效!!! sqlSession.commit();

6.错误

  1. namespace="com.liu.Mapper.UserMapper";必须写详细路径

  2. sql语句标签错误

  3. 注册mapper;resource的路径要用/而不是点

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

4.maven资源导出问题

<build>
        <resources>
<!--            设置正常情况的resources目录下的properties文件-->
            <resource>
<!--                配置路径-->
                <directory>src/main/resources</directory>
                <includes>
<!--                    包含什么文件-->
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
            </resource>
<!--      设置java路径的properties文件-->
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
    </build>

7.非常实用的Map

如果我们的实体类或数据中的表,字段过多,我们考虑使用Map。

修改或添加部分信息:

UserMapper:

//使用map修改部分数据
    public int alterUser2(Map<String,Object> map);

UserMapper.xml

<update id="alterUser2" parameterType="map">
        update user set  name=#{username}  where id=#{userid};
    </update>

Test:

    //使用map更新部分信息
    @Test
    public void alterUser2(){
        SqlSession sqlSession = MybatisUtil.getsqlSession();
​
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("username","李小盛");
        map.put("userid",3);
        int i = mapper.alterUser2(map);
        if (i>0)
            System.out.println("修改成功");
        else
            System.out.println("修改失败");
​
        //提交事务
        sqlSession.commit();
        sqlSession.close();
    }

根据多个条件查询信息:

UserMapper:

 //指定多条件查询
    public User getUserById2(Map<String,Object> map);

UserMapper.xml

 <select id="getUserById2" parameterType="map" resultType="com.liu.Pojo.User">
        select * from user where id=#{id} and name=#{name}
    </select>

Test:

  @Test
    public void getUserById2(){
        SqlSession sqlSession = MybatisUtil.getsqlSession();
​
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("id",3);
        map.put("name","李小盛");
        User user = mapper.getUserById2(map);
​
        System.out.println(user);
​
        sqlSession.close();
    }

Map传递参数,直接在sql中取出key即可。

使用Map的时候一定要注意map里面的键值对必须和UserMapper.xml中使用的变量对应!!!

对象传递参数,直接在sql中去对象的属性。

只有一个基本数据类型参数的情况下,可以直接在sql中使用

多个参数用map或注解!

8.模糊查询

1.java执行的时候传递通配符%

List<User> users = mapper.likeUser("%李%");

2.在sql拼接中使用通配符

<select id="likeUser" resultType="com.liu.Pojo.User">
    select * from user where name like "%"#{name}"%"
</select>

第二种写法:

<select id="queryAll" resultMap="USER" parameterType="map">
    select * from user
    <where>
        <if test="name!=null">
            name like '%${name}%'
        </if>
    </where>
</select>

4.XML配置

1.核心配置文件

mybaits-config,xml

2.环境配置

Mybatis可以配置成适应多环境

注:尽管可以配置多个环境,但是每个sqlSessionFactory实例只能选择一种环境

学会使用配置多套运行环境!

Mybatis默认的事务管理器就是JDBC,连接池:POOLED

3.属性(properties)

通过propertis来引入配置文件

1.编写一个配置文件:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///mybatis
username=root
password=123456

2.在核心配置文件中引入:在核心配置文件中所有的标签都有规定的先后顺序

资源在核心配置同一个路径下可以使用相对路径定位

没有添加额外参数
<properties resource="db.properties"/>
​
添加额外参数
<properties resource="db.properties">
        <property name="username" value="root1"/>
        <property name="password" value="123456"/>
    </properties>
  • 可以直接引入配置文件

  • 可以在其中增加一些配置属性

  • 如果两种方式有相同的属性,优先使用外部配置文件的属性!!!

4.类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写

第一种方式:

<typeAliases>
        <typeAlias type="com.liu.Pojo.User" alias="User"/>
    </typeAliases>

第二种方式

可以指定一个包名,Mybatis会在这个包名下面搜索需要的JavaBean,比如:

扫描的实体类的名称,它的默认别名就是这个类的首字母小写。

<typeAliases>
            <package name="com.liu.Pojo"/>
        </typeAliases>x

在实体类比较少的情况使用第一种,

如果实体类十分多的时候,建议第二种

第一种可以DIY别名,第二种不行。如果非要在实体类上增加注解,注解方式大于默认搜索

@Alias("hello")
public class User(){};

5.映射器(mappers)

MapperRegistry:注册绑定我们需要的Mapper文件

方式一:【推荐使用】

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

方式二:使用class方式绑定注册

<mappers>
        <mapper class="com.liu.Mapper.UserMapper"/>
    </mappers>

方式三:使用包扫描注册绑定

 <mappers>
        <package name="com.liu.Mapper"/>
    </mappers>

方式二和方式三注意!!:

  • 接口和它的Mapper配置文件必须同名

  • 接口和它的Mapper配置文件必须在同一个包下面

6.生命周期

 

生命周期类和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题。

SqlSessionFactoryBuilder:

  • 一旦创建了SqlSessionFactory就不在需要他了

  • 局部变量

SqlSessionFactory:

  • 可以想象为数据库连接池

  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。

  • 因此 SqlSessionFactory 的最佳作用域是应用作用域。(程序开始他就开始,程序结束他就结束)

  • 简单的就是使用单例模式或者静态单例模式。

SqlSession

  • 连接到连接池的一个请求!

  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域

  • 用完之后赶紧关闭,否则资源被占用

 

每一个Mapper代表一个具体的业务。

5.解决属性名和字段名不一致的问题

解决方法:

  • 起别名

<select id="getUserById" parameterType="int" resultType="com.liu.Pojo.User">
        select id,name,password as pwd from user where  id = #{id}
    </select>

2.resultMap

结果集映射

id name password
id name pwd

resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。

ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

<!--   resultMap就是使用这里的id,类型映射为实体类 -->
    <resultMap id="UserMap" type="User">
<!--       property表示实体类属性,column表示数据库字段映射。将字段映射为实体类的对应属性 -->
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="pwd" column="password"/>  
    </resultMap>
    <select id="getUserList" resultMap="UserMap">
    select * from user
  </select>

6.日志

6.1日志工厂

如果一个数据库操作出现了异常,我们需要排错。日志就是我们最好的助手。

曾经:sout、debug

现在:日志工厂!

 

  • SLF4J

  • LOG4J【掌握】

  • LOG4J2

  • JDK_LOGGING

  • COMMONS_LOGGING

  • STDOUT_LOGGING【掌握】

  • NO_LOGGING

在Mybatis中具体使用哪一个日志实现,在设置中设定!

STDOUT_LOGGING:标准日志输出

配置设置的时候一定要注意格式、大小写、空格等问题

<settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

6.2Log4j

Apache的开源项目log4j是一个功能强大的日志组件,提供方便的日志记录。

  • 日志可以输出到控制到或GUI组件

  • 我们可以控制每一条日志输出的格式

  • 通过设置每一条日志的级别,我们能够更细致的控制日志生成的过程。

  • 通过配置文件来进行配置,不需要我们修改应用的代码

1.导入LOG4J的包

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

2.log4j.properties

### 配置根 ###
#配置日志类型
log4j.rootLogger = debug,console ,file

### 设置输出sql的级别,其中logger后面的内容全部为jar包中所包含的包名 ###
log4j.logger.org.apache=debug
log4j.logger.java.sql.Connection=debug
log4j.logger.java.sql.Statement=debug
log4j.logger.java.sql.PreparedStatement=debug
log4j.logger.java.sql.ResultSet=debug

### 配置输出到控制台 ###
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 =  %d{ABSOLUTE} %5p %c{ 1 }:%L - %m%n

### 配置输出到文件 ###
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File = ./logs/mybatis.log
log4j.appender.file.Append = true
log4j.appender.file.Threshold = DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 配置输出到文件,并且每天都创建一个文件 ###
log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile.File = logs/log.log
log4j.appender.dailyRollingFile.Append = true
log4j.appender.dailyRollingFile.Threshold = DEBUG
log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %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.PraparedStatement=DEBUG

3.配置log4j为日志的实现

<settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

4.Log4j的使用,直接测试运行查询就行了

简单使用

1.在需要使用log4j的类中,导入包,包不要导错了,需要导入apeach的log4j

2.生成日志对象,加载参数为当前类的class

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

3.日志级别

logger.info("info:进入testlog4j");
logger.debug("debug:进入testlog4j");
logger.error("error:进入testlog4j");

7.分页

为什么要分页?

  • 减少数据的处理量

#{参数名}:按照preparedstatement解析sql语句时所使用的的?占位符

${参数名}:传什么参数,就按字符串拼接方式进行填充

7.1使用limit分页,语法:

select * from user limit statIndex,pagesize;

select * from user limit 2,5
select * from user limit 3

使用mybatis实现分页,核心SQl

1.接口

//分页查询
public List<User> limitUser(Map<String,Integer> map);

2.Mapper.xml

<!--    实现分页查询-->
    <select id="limitUser" parameterType="map" resultType="user">
        select * from user limit #{startIndex},#{pageSize}
    </select>

3.测试

//分页查询
@Test
public void limitUser(){
    SqlSession sqlSession = MybatisUtil.getsqlSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("startIndex",0);
    map.put("pageSize",3);
    List<User> users = mapper.limitUser(map);
    for (User user : users) {
        System.out.println(user);
    }
}

7.2useGeneratedKeys

如果想要拿到插入数据后自动递增的主键值时,使用useGeneratedKeys=true

KeyColumn:指定自动递增的列明

KeyProperty:指定传入的参数对象中用于存放自动递增的值对应的属性

8.使用注解开发

8.1面向接口编程

  • 接口定义与实现的分离

  • 接口的本身反应了系统设计人员对系统的抽象理解

  • 接口也应有两个类:

    • 一个是对一个个体的抽象abstract

    • 一个是对个体某个方面的抽象interface

8.2使用注解开发

1.注解直接在接口上实现

@Select("select * from user")
List<User> getUsers();

2.需要在核心配置文件中绑定接口!

<mappers>
    <mapper class="com.liu.Mapper.UserMapper2"/>
</mappers>

3.测试

//注解方式
@Test
public void zjUsers(){
    SqlSession sqlSession = MybatisUtil.getsqlSession();

    UserMapper2 mapper = sqlSession.getMapper(UserMapper2.class);
    List<User> users = mapper.getUsers();
    for (User user : users) {
        System.out.println(user);
    }

    sqlSession.close();
}

本质:反射机制实现

底层:动态代理

8.3CRUD

我们可以在工具类创建的时候实现自动提交事务!

return sqlSessionFactory.openSession(true);

编写接口,增加注解

 //方法存在多个参数时,所有的参数前面必须加上@Param("id")注解
    @Select("select * from user where id=#{id} and name=#{name}")
    public User getUserById(@Param("id") int id,@Param("name") String username);

    @Insert("insert into user values (#{id},#{name},#{password})")
    public int addUser(User user);

    @Update("update user set name=#{name},password=#{password} where id=#{id}")
    int UpdateUser(User user);

    @Delete("delete from user where id=#{id}")
    public int delUser(@Param("id") int pid);

【我们必须将接口绑定到注册到我们的核心配置文件中】

<mappers>
        <package name="com.liu.Mapper"/>
    </mappers>

关于@Param(")注解

  • 基本类型的参数或则String类型的参数需要加上

  • 引用数据类型不需要加

  • 如果只有一个数据类型的话可以忽略,建议也加上

  • 我们在SQL中引用的就是我们这里的@Param设置的属性名

#{}和${}区别:

  • #{}和${}这两个语法是为了动态传递参数而存在的,是Mybatis实现动态SQL的基础

  • #{} 占位符 :动态解析 -> 预编译 -> 执行

  • ${} 拼接符 **:动态解析 -> 编译 -> 执行

类似statement和preparestatement

9.Lombok

  • java library

  • plugs

  • build tools

使用步骤:

1.在IDEA安装plug,Lombok插件

2.在maven中导入Lombok依赖

 <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>

@Data:,toString,GetandSet,hashcode,equals

@AllArgsConstructor
@NoArgsConstructor

全参构造和无参构造

3.在实体类上加注解即可

10.多对一

  • 对学生来说,关联,多个学生关联一个老师【多对一】

  • 对老师而言,集合,一个老师辅导多个学生【一对多】

环境搭建:

1.导入Lombok

2.新建实体类Teacher,Student

3.建立Mapper接口

4.建立Mapper.xml文件

5.在核心配合文件中绑定Mapper接口或文件

6.测试

需求查询所有的学生信息以及对应的老师信息。

1.按照查询嵌套处理

<!--    使用结果集映射需要使用resultMap-->
<select id="getStudent" resultMap="StudentTeacher">
    select * from student
</select>
<resultMap id="StudentTeacher" type="student">
    <!--        复杂的属性,我们需要单独处理-->
    <!--        对象使用association-->
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <association property="teacher" column="id" javaType="Teacher" select="getTeacher"/>
    <!--        集合使用collection-->
    <!--        <collection property=""-->
</resultMap>

<select id="getTeacher" resultType="teacher">
    select * from teacher where id=#{id}
</select>

 

第二条select语句中的where里面的条件#{},花括号里面随便写都不会影响查询结果

2.按照结果嵌套处理

<!--    按照结果嵌套处理-->
<select id="getStudent2" resultMap="StudentTeacher2">
    select s.id  sid,s.name  sname,t.name  tname
    from student as s,teacher as t where s.id=t.id
</select>

<resultMap id="StudentTeacher2" type="student">
    <result column="sid" property="id"/>
    <result column="sname" property="name"/>
    <association property="teacher" javaType="Teacher">
        <result property="tid" column="id"/>
        <result property="name" column="tname"/>
    </association>
</resultMap>

查询结果:

Student(id=1, name=liulang, cno=1, teacher=Teacher(id=1, name=chentao, cno=1)) Student(id=2, name=lixiaosheng, cno=1, teacher=Teacher(id=1, name=chentao, cno=1)) Student(id=3, name=zhenghaiqing, cno=1, teacher=Teacher(id=1, name=chentao, cno=1))

11.一对多

一个老师拥有多个学生

1.结果集映射

<select id="getTeacher2" resultMap="TeacherStudent">
    select s.id sid,s.name sname,t.name tname,t.id tid,t.cno tcno,s.cno scno
    from student s,teacher t where s.cno=t.cno
</select>
<resultMap id="TeacherStudent" type="teacher">
    <result property="id" column="tid"/>
    <result property="cno" column="tcno"/>
    <result property="name" column="tname"/>
    <collection property="students" ofType="student">
        <result property="id" column="sid"/>
        <result property="cno" column="scno"/>
        <result property="name" column="sname"/>
    </collection>
</resultMap>

2.子查询方式

<!--    子查询-->
    <select id="getTeacher" resultMap="TeacherStudent1">
        select * from teacher
    </select>
    <resultMap id="TeacherStudent1" type="Teacher">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="cno" column="cno"/>
<!--      column用户传递给子查询作为where条件  -->
        <collection property="students" column="cno" javaType="ArrayList" ofType="Student" select="getStudent"/>
    </resultMap>
    <select id="getStudent" resultType="Student">
        select * from student where cno=#{cno}
    </select>

查询结果:

Teacher(id=1, name=chentao, cno=1, students=[Student(id=1, name=liulang, cno=1), Student(id=2, name=lixiaosheng, cno=1), Student(id=3, name=zhenghaiqing, cno=1)])

总结:

1.关联-association【多对一】

2.集合-collection【一对多】

3.javaType & ofType

1.javaType用来指定实体类中属性的类型

2.ofTyp用来指定映射到list或则集合中的pojo类型,泛型中的集合类型

注意:

  • 保证SQL的可读性,尽量保证通俗易懂

  • 注意一对多和多对一中,属性名和字段的问题

  • 如果问题不好排查,可以使用日志,建立使用log4j

12.动态SQL

什么是动态SQL:根据不同的条件生成不同的SQL语句

所谓动态SQL:本质还是SQL语句,只是我们可以再SQL层面,去执行一个逻辑代码

1.搭建环境

开启驼峰命名:只应用于数据库映射java使用的驼峰命名

 <settings>
        <!--        开启驼峰命名映射-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

2.where

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

使用where标签如果你满足的第一个条件在where 后面添加了一个and,会被这个标签自动删除and然后正常执行,如果你没有过滤条件,那么where标签会将where字句删除

<where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>

3.IF

BlogMapper.xml

<insert id="addBlog" parameterType="blog">
        insert into blog values(#{id},#{title},#{author},#{createTime},#{view});
    </insert>

    <select id="queryBlogIf" parameterType="map" resultType="blog">
        select * from blog where 1=1
        <if test="title != null">
           and title=#{title}
        </if>
        <if test="author != null">
             and author = #{author}
        </if>
    </select>

test:

    public void queryBlogIf(){
       SqlSession sqlSession = MybatisUtil.getsqlSession();

       BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
       HashMap map = new HashMap();
       map.put("title","Mybatis");
//       map.put("author","ll");
       List<Blog> blogs = mapper.queryBlogIf(map);
       for (Blog item : blogs) {
           System.out.println(item);
       }

       sqlSession.close();
   }

4.choose(swtich)

<select id="queryBlogChoose" resultType="blog" parameterType="map">
        select * from blog
        <where>
            <choose>
                <when test="title != null">
                    title=#{title}
                </when>
                 <when test="author != null">
                     author=#{author}
                </when>
                <otherwise>
                    view > 0
                </otherwise>
            </choose>

        </where>
    </select>

作用:和java的swtich功能类似

  • 只会满足其中之一,如果存在满足多个条件的话也只会满足第一个条件

5.Set

作用:set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号```xml

<update id="updateBlogSet" parameterType="map" >
    update blog
    <set>
        <if test="title != null">
            title=#{title},
        </if>
        <if test="author != null">
            author=#{author},
        </if>
        <if test="view != null">
            view=#{view},
        </if>
    </set>
    where id = '35a166859fb84884b1e18732a05516ff'
</update>

6.foreach(几乎不用)

 

我们想查出数据库前两条数据

<select id="queryfreeBlog" resultType="Blog">
        select * from Blog
        <where>
            <foreach collection="list" item="id" open="and (" close=")" separator="or">
                id = #{id}
            </foreach>
        </where>
    </select>

open:循环开始之前拼接的内容;

close结:循环结束拼接的内容

separator:指定循环中的分隔符

collection:循环遍历的内容

item:遍历的单个变量,在标签体重来使用

@Test
    public void queryfreeBlog(){
       SqlSession sqlSession = MybatisUtil.getsqlSession();

       BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
       HashMap map = new HashMap();
       ArrayList<String> str = new ArrayList<String>();
       str.add("35a166859fb84884b1e18732a05516ff");
       str.add("6d5cb2ff745c4f658eefd4b21add44f9");
       map.put("list",str);
       List<Blog> blogs = mapper.queryfreeBlog(map);
       for (Blog blog : blogs) {
           System.out.println(blog);
       }

       sqlSession.close();
   }

结果:

Blog{id='35a166859fb84884b1e18732a05516ff', title='这不是结局', author='小新', createTime=2021-07-29, view=998} Blog{id='6d5cb2ff745c4f658eefd4b21add44f9', title='java', author='刘星', createTime=2021-07-29, view=1005}

注意:

如果这里我们没有传递任何参数,就会自动删除where条件,将会查出所有信息

7.SQL片段

有的时候,我们可能会将一些功能的部分抽取出来,方便复用

 <sql id="if-author-title">
        <if test="title != null">
            and title=#{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </sql>
    <select id="queryBlogIf" parameterType="map" resultType="blog">
        select * from blog
        <where>
            <include refid="if-author-title"/>
        </where>
    </select>

实现代码的复用

使用sql标签提取公共部分

在使用的地方用include标签引入SQL片段通过id引入

注意:

  • 最好基于单表来定义SQL片段

  • 不要存在where标签

  • 最好只要一些IF判断就好了

13.缓存(了解)

1.什么是缓存?

  • 存在内存中的临时数据

  • 将用户数据查询到的数据放在缓存中,用户去查询数据就不用从磁盘上查询,从缓存中读取,从而提高效率,解决了高并发系统的性能问题

2.为什么要使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率

3.什么样的数据能使用缓存?

查询:连接数据库,耗内存

解决:一次查询的结果,给他暂存在一个可以直接读取的地方--》内存:缓存

我们再次查询相同数据的时候我们走缓存,就不用走数据库了。

14.Mybatis缓存

  • Mybatis包含一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存,缓存可以极大地提升查询效率

  • Mybatis系统中默认定义了两级缓存:一级缓存和二级缓存

  • 默认情况下,只有一级缓存开启。(本地缓存)

  • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存

  • 为了提高扩展性,mybatis定了缓存接口Cache,我们可以通过实现Cache接口来定义二级缓存

一级缓存

  • 一级缓存也叫本地缓存

  • 与数据库同义词会话期间查询到的数据会被放在本地缓存中

  • 以后如果需要获取相同的数据的话,直接从缓存中拿,没有必要在去查数据库

实验步骤

1.开启日志

2.测试在一个Session中查询两次相同的记录

3.查看日志输出

 @Test
    public void queryAllUser(){
        SqlSession sqlSession = MybatisUtil.getsqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.queryAllUser(6);
        System.out.println(users);

        System.out.println("=============================================");
        List<User> users1 = mapper.queryAllUser(6);
        System.out.println(users1);
        System.out.println(users==users1);


        sqlSession.close();
    }

 

增删改会刷新原来的东西

 

缓存失效的情况:

  • 增删改都有可能改变原来的数据,必定会刷新缓存

  • 查询不同的Mapper.xml

  • 手动清理缓存

  • 查询不同的东西

小结:一级缓存默认是开启的,只在一次SqlSessionFactory中有效,也就是拿到链接到关闭连接区间有效。一级缓存也是无法关闭的。

一级缓存作用域就是sqlsession对象,存储的内容,查询后的结果集,在sqlSession对象中有Map结果,Map的值就是结果集的对象。

二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存

  • 工作机制:

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中

    • 如果当前会话关闭了,这个会话对应的一级缓存就没有了;但是我们想要的是,会话关闭了,一级缓存中的数据会被保存到二级缓存中

    • 新的会话查询信息,就可以从二级缓存中获取内容;

默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:<cache/>

步骤:

1.开启缓存设置

<setting name="cacheEnabled" value="true"/>

2.要是用二级缓存需要在Mapper中开启

<!--在当前的xml中开启二级缓存-->
    <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

也可以自定义一些参数

3.测试

 @Test
    public void queryAllUser(){
        SqlSession sqlSession1 = MybatisUtil.getsqlSession();
        SqlSession sqlSession2 = MybatisUtil.getsqlSession();
        UserMapper mapper = sqlSession1.getMapper(UserMapper.class);
        List<User> users = mapper.queryAllUser(6);
        System.out.println(users);
        sqlSession1.close();



        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
        List<User> users2 = mapper2.queryAllUser(6);
        System.out.println(users2);

        sqlSession2.close();
    }

 

只进行了一次查询

小结:二级缓存只能在一个namespace内有效,也就是只在一个xml文件内。所有的数据都会先放在一级缓存中,只有当会话提交或关闭的时候转存到二级缓存

问题!:我们需要将实体类序列化

implements Serializable

查找顺序

 

自定义缓存--ehcache

ehcache是一种广发使用的开源java分布式缓存

Mybatis注意点

mybatis-config.xml:

 

MybatisUtil.java

 

UserMapper.xml

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值