Mybatis

Mybatis

简介:

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。

MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。

MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录

1.JDBC访问数据库的步骤:

1.加载数据库驱动 Driver

2.获取连接 Connection

3.获取sql执行器 Statement,PrepareStatement

4.执行sql,得到结果集 ResultSet

5.javaBean映射操作

6.关闭连接

内部组织结构:

Mybatis

核心原理:

Mybatis

2.核心对象:

SqlSessionFactoryBuilder

用来加载配置文件,调用build方法创建SqlSessionFactory对象

SqlSessionFactoryBuilder是通过xml配置文件或者configuration类的实例去构造这个对象。然后调用build()方法去构造SqlSessionFactory对象。

用过即丢,其生命周期只存在于方法体内
可重用其来创建多个SqlSessionFactory实例
负责构建SqlSessionFactory,并提供多个build方法的重载

SqlSessionFactory

SqlSessionFactory对象调用openSession方法获取SqlSession对象

SqlSessionFactory是我们MyBatis整个应用程序的中心;

整个程序都是以SqlSessionFactory的实例为核心的,

使用SqlSessionFactory对象的openSession()方法来获取SqlSession对象,

用SqlSession对象去操作数据库

因为SqlSession里面包含了以数据库为背景所有执行sql操作的方法。

SqlSession

包含了执行sql所需的所有方法
对应一次数据库会话,会话结束必须关闭
线程级别,不能共享

在SqlSession里可以执行多次SQL语句,但一旦关闭了SqlSession就需要重新创建。

SqlSession实例不能被共享,也不是线程安全的。所以其最佳作用域是reqeust作用域内或者方法体内的作用域内。

3.Mybatis配置形式流程:

3.1配置依赖

<!--?xml version="1.0" encoding="UTF-8"?-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelversion>4.0.0</modelversion>
    
    <groupid>com.iweb</groupid>
    <artifactid>mybatis</artifactid>
    <version>1.0-SNAPSHOT</version>
    
<dependencies>
    <dependency>
        <groupid>mysql</groupid>
        <artifactid>mysql-connector-java</artifactid>
        <version>5.1.39</version>
    </dependency>
    <dependency>
        <groupid>c3p0</groupid>
        <artifactid>c3p0</artifactid>
        <version>0.9.1.2</version>
    </dependency>
    <!--mybatis依赖-->
    <dependency>
        <groupid>org.mybatis</groupid>
        <artifactid>mybatis</artifactid>
        <version>3.2.8</version>
    </dependency>
    <dependency>
        <groupid>junit</groupid>
        <artifactid>junit</artifactid>
        <version>4.11</version>
    </dependency>
</dependencies>

</project>

3.2创建实体类

package com.iweb.entity;

/**
 **/
public class User {
    private Integer id;
    private String name;
    private Integer age;
public Integer getId() {
    return id;
}

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

public String getName() {
    return name;
}

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

public Integer getAge() {
    return age;
}

public void setAge(Integer age) {
    this.age = age;
}

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

3.3编写核心配置文件sqlMapConfig.xml

需要配置环境environment,

事务管理器transactionManager,

数据源POOLED,

引入映射文件,请看第五步

<!--?xml version="1.0" encoding="UTF-8" ?-->

<!--configuration核心配置标签-->
<configuration>

    <!--environments环境配置-->
    <environments default="mysql"><!--环境名任意取,要和下面的对应即可-->
        <environment id="mysql">
            <!--transactionManager事务管理器,type=JDBC使用jdbc管理事务-->
            <transactionmanager type="JDBC">
            <!-- 配置数据源 type="POOLED"使用mybatis自带的连接池-->
            <datasource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver">
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis">
                <property name="username" value="root">
                <property name="password" value="123456">
            </property></property></property></property></datasource>
        </transactionmanager></environment>
    </environments>
</configuration>

3.4编写映射文件,编写sql

这个配置文件写在resource目录中

namespace 其实就是实现对应的接口,和jdbc的接口实现类相似

select标签中的id,就相当于jdbc实现类的方法名;resultType 返回的javaBean类型

<!--?xml version="1.0" encoding="UTF-8" ?-->


<!--namespace命名空间,给SqlSession提供可以识别区分不同的命名空间
	这个namespace	其实就是实现对应的接口,和jdbc的接口实现类相似
-->
<mapper namespace="com.iweb.mapper.UserMapper">
    <!--id 唯一定义标识,在同一命名空间下,id不能重复
        resultType 定义执行这条sql语句后,返回的javaBean类型
        select标签 用来定义查询语句
    -->
    <select id="getAllUser" resulttype="com.iweb.entity.User">
        select * from user
    </select>
    <!--parameterType 定义参数的类型-->
    <select id="getUserById" parametertype="java.lang.Integer" resulttype="com.iweb.entity.User">
        select * from user where id=#{id}
    </select>
    <!--当接收javaBean对象参数时,#{}在取值时,需和实体中的属性名相同,且要提供get、set方法-->
    <insert id="addUser" parametertype="com.iweb.entity.User">
        insert into user (username,password,nickname,email,phone,gender,status)
        values (#{username},#{password},#{nickname},#{email},#{phone},#{gender},#{status})
    </insert>

    <insert id="addUser" parametertype="com.iweb.entity.User">
        insert into user (username,password,nickname,email,phone,gender,status)
        values (#{username},#{password},#{nickname},#{email},#{phone},#{gender},#{status})
    </insert>
</mapper>

3.5将映射文件配置到sqlMapConfig.xml中

每一个Mapper.xml都需要在Mybatis核心配置文件中注册,需要用到mapper标签

<!--?xml version="1.0" encoding="UTF-8" ?-->

<!--configuration核心配置标签-->
<configuration>

    <!--environments环境配置-->
    <environments default="mysql"><!--环境名任意取,要和下面的对应即可-->
        <environment id="mysql">
            <!--transactionManager事务管理器,type=JDBC使用jdbc管理事务-->
            <transactionmanager type="JDBC">
            <!-- 配置数据源 type="POOLED"使用mybatis自带的连接池-->
            <datasource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver">
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis">
                <property name="username" value="root">
                <property name="password" value="123456">
            </property></property></property></property></datasource>
        </transactionmanager></environment>
    </environments>

    <!--导入mapper.xml配置
        配置映射文件
    -->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"></mapper>
        <!--使用class属性指向需要使用注解的包路径或类路径-->
    	<mapper class="com.iweb.mapper.UserMapperAnnotation"></mapper>
    </mappers>
</configuration>

3.6测试

1.获取mybatis主配置文件,得到一个输入流

2.创建SqlSessionFactoryBuilder对象

3.通过builder的build方法加载配置文件,创建SqlSessionFactory对象

4.获取SqlSession对象,支持事务,但不提交

​ factory.openSession(true);设置自动提交事务

测试类其实就三步:

1.获得一个sqlSession

2.获取mapper,拿到对应的mapper对象

3.通过mapper对象就可以调用接口中定义的方法

public void test1() throws Exception{
    //1.获取mybatis主配置文件,得到一个输入流
    InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
    //2.创建SqlSessionFactoryBuilder对象
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    //3.通过builder的build方法加载配置文件,创建SqlSessionFactory对象
    SqlSessionFactory factory = builder.build(in);
    //4.获取SqlSession对象,支持事务,但不提交
    SqlSession session = factory.openSession();
    //factory.openSession(true);//设置自动提交事务

    
    // 里面执行sql语句的select标签id为getAllUser
    List<object> list = session.selectList("test.getAllUser");
    
    //更多使用自己配置的接口
    UserMapper mapper = session.getMapper("UserMapper.xml");
    User allUser = mapper.getAllUser;
    //test.getAllUser:映射xml中的命名空间test
    System.out.println(list);
}

4.Mybatis注解形式:

4.1创建核心配置文件 sqlMapConfig.xml

<!--?xml version="1.0" encoding="UTF-8" ?-->

<!--configuration核心配置标签-->
<configuration>

    <!--environments环境配置-->
    <environments default="mysql"><!--环境名任意取,要和下面的对应即可-->
        <environment id="mysql">
            <!--transactionManager事务管理器,type=JDBC使用jdbc管理事务-->
            <transactionmanager type="JDBC">
            <!-- 配置数据源 type="POOLED"使用mybatis自带的连接池-->
            <datasource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver">
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis">
                <property name="username" value="root">
                <property name="password" value="123456">
            </property></property></property></property></datasource>
        </transactionmanager></environment>
    </environments>

    <!--导入mapper.xml配置
    classpath:找到模块的resource路径
    -->
    <mappers>
        <!--使用class属性指向需要使用注解的包路径或类路径-->
        <mapper class="com.iweb.mapper.UserMapperAnnotation"></mapper>
    </mappers>
</configuration>

4.2在mapper接口中使用注解

package com.iweb.mapper;

import com.iweb.entity.User;

import java.util.List;
import java.util.Map;
/**
 * 注解形式mapper
 */
public interface UserMapperAnnotation {
    @Select("select * from user")
    public List<user> getAllUser();

    @Select("select * from user where id=#{id}")
    public User getUserById(Integer id);

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

4.3配置一个utils工具类,不用重复写获得sqlSession对象的代码

public static <t> T getAnnotationMapper(Class<t> clz){
    InputStream in = null;
    try{
        //1.获取mybatis主配置文件,得到一个输入流
        in = Resources.getResourceAsStream("mybatis-Config.xml");
        //2.创建SqlSessionFactoryBuilder对象
        // 通过builder的build方法加载配置文件,创建SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.获取SqlSession对象
        SqlSession session = factory.openSession(true);
        return session.getMapper(clz);
    }catch (IOException e){
        e.printStackTrace();
    }finally {
        try {
            in.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    return null;
}

自己写的工具类:

public class MybatisUtils {
    //获取SqlSession
    public static SqlSession getSqlSession() throws IOException {
        //获取文件流
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");

        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        SqlSession session = factory.openSession(true);
        return session;
    }
}

注解形式的测试:

@Test
public void test1(){
    UserMapperAnnotation mapper = MybatisUtil.getAnnotationMapper(UserMapperAnnotation.class);
    List<user> allUser = mapper.getAllUser();
    System.out.println(allUser);
}
@Test
public void test2(){
    UserMapperAnnotation mapper = MybatisUtil.getAnnotationMapper(UserMapperAnnotation.class);
    System.out.println(mapper.getUserById(2));
}
@Test
public void test3(){
    UserMapperAnnotation mapper = MybatisUtil.getAnnotationMapper(UserMapperAnnotation.class);
    mapper.deleteUser(2);
}

5.sql中的取值方式:

#{}与${}

{}特点:

会作为sql参数使用,替代掉sql中的?

有预编译机制,防止sql注入,效率高

字符串类型,日期类型,会自动两边拼单引号

insert into user values(null,#{name},55) -->insert into user values(null,'张三',55)
insert into user values(null,${name},55) -->insert into user values(null,张三,55) 
--sql语句错误  

${}特点:

会原样直接拼接到sql上

没有预编译机制,不能防止sql注入,效率低

字符串类型,日期类型,不会自动两边拼单引号

select * from user order by #{name}--> select * from user order by 'name'//sql错误 
select * from user order by ${name}--> select * from user order by name

总结:

通常都使用#{},可以防止sql注入,效率高,但如果传递的是列名,表名,关键字等必须直接拼接到sql的内容时,使用${},防止自动拼接单引号改变sql语义

6.参数的传值:

map传递参数,直接在sql中取出key即可 parameterType="map"

对象传递参数,直接在sq中取出对象的属性即可 parameterType="Object"

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

多个参数用map,或者注解

6.1单值传递

6.2map传值

当实体类或数据库中的表字段或参数过多时,考虑使用map传值。这样可以不用把所有的字段都写出来

比如说,数据库某表中有100个字段,当要修改数据库表中某一条记录的某一个字段时,使用map传值,就只需要写要改的字段即可。而如果是单值传递,就要把所有字段全部写上。

<insert id="updateUser" parametertype="map">
    <!--此处values其实就是map中的键,也是表字段名-->
	insert into user (id,password) values(#{id},#{password})
</insert>

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

@Test
public void test2() throws IOException {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Map<string, object=""> map = new HashMap<>();
    //put的键对应xml中的values的键,为了代码可读性,还是要求名字相同
    map.put("id","6");
    map.put("password","tihuandemima");
    mapper.updateUser(map);
}

模糊查询:

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

List<user> userList = mapper.getUserList("%李%");

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

<select id="getUserList" resulttype="com.iweb.domain.User" select="" *="" from="" user="" where="" name="" like="" "%"#{name}"%"="" <="">

7.配置优化解析

7.1环境配置:

mybatis可以配置成适应多种环境

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

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

7.2属性:properties

通过properties属性来实现引用配置文件

这些属性都是可以通过外部配置动态替换的。

编写一个配置文件:db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8
username=root
password=123456

在核心配置文件中引入

properties标签必须写在最上面

<!--?xml version="1.0" encoding="UTF-8" ?-->
        
        <!--configuration核心配置标签-->

<!--引入外部配置文件,properties放在resource目录下-->
    
<!--environments环境配置-->
<!--环境名任意取,要和下面的对应即可-->
    
        <!--transactionManager事务管理器,type=JDBC使用jdbc管理事务-->
        
        <!-- 配置数据源 type="POOLED"使用mybatis自带的连接池-->
        
            
            
            
            
        
    


7.3别名:typeAliases

放在configuration标签的第三个位置

类型别名是为java类型设置一个短名字

存在的意义仅在于减少类全名的冗余

3.1给实体类起别名


    

实体类起别名之后就不用写全类名了,就写别名

</select>
    select * from user

3.2也可以指定一个包名,mybatis会在包名下搜索需要的javaBean

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

<typealiases>
    <package name="com.iweb.domain">
</package></typealiases>

区别:

在实体类比较少的时候,使用第一种

当实体类特别多,建议使用第二种

第一种可以diy别名,第二种不行,如果非要改,需要在实体上增加注解@Alias

7.4映射器:mappers

每一个mapper.xml都需要在mybatis核心配置文件中注册

注册绑定我们的mapper文件

方式1:resource资源路径绑定 ——[推荐使用]

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

方式2:使用class文件绑定注册

<mappers>
	<mapper class="com.iweb.mapper.Usermapper">
</mapper></mappers>

注意

接口和他的mapper配置文件必须同名

接口和他的mapper配置文件必须在同一个包下

方式3:使用扫描包进行注入绑定

<mappers>
	<package name="com.iweb.mapper">
</package></mappers>

注意

接口和他的mapper配置文件必须同名

接口和他的mapper配置文件必须在同一个包下

7.5生命周期和作用域

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

SqlSessionFactoryBuilder:

一旦创建了SqlSessionFactory,就不再需要它了

一般做成员变量

SqlSessionFactory

说白了就是:数据库连接池

SqlSessionFactory一旦被创建,就会在应用的运行期间一直存在,没有任何理由丢弃或重新创建。因为丢弃和重新创建都会浪费内存资源

SqlSessionFactory的最佳作用域是application,也就是整个应用中,直到应用关闭

最简单的使用就是单例模式,保证全局只有一个

SqlSession

连接到连接池的一个请求

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

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

一个SqlSessionFactory可以创建多个SqlSession,

每个SQLSession可以获得多个mapper,

这里的每一个mapper,就代表一个具体的业务

8.解决属性名和字段名不一致的问题:resultMap

实体类:

public class User {
    private Integer id;
    private String username;
    private String pwd;//将这里的属性名和数据库字段名设置不一致
    private String nickname;
}

mapper.xml

<select id="getUserList" resulttype="user">
    select id,username,password from user
</select>

当获取信息后,由于属性名和字段名不一致,pwd的值会为空

解决方法:

方式1:起别名

<select id="getUserList" resulttype="user">
    select id,username,password pwd from user
</select>

方式2:resultMap结果集映射

resultMap标签中

type:就是返回的数据类型

id:给select标签中的resultMap属性指定结果集映射

resultMap标签中的result标签

column:数据库表字段名;property:实体类属性名

<resultmap id="userMap" type="user">
    <!--column:数据库中字段名。property:实体类中属性名-->
    <result column="password" property="pwd">
</result></resultmap>

<select id="getUserList" resultmap="userMap">
    select * from user where id=#{id}
</select>

9.日志工厂:

主要用来排错,把错误的信息用日志记录

在mybatis核心配置文件中配置settings

<settings>
    <!--标准日志工厂实现-->
    <setting name="logImpl" value="STDOUT_LOGGING">
</setting></settings>

log4j:

可以将日志信息传送到控制台、文件、GUI组件中

可以控制每一条日志的输出格式,通过一个配置文件来灵活进行配置(log4j.properties)

1.导入log4j依赖

<dependency>
    <groupid>log4j</groupid>
    <artifactid>log4j</artifactid>
    <version>1.2.17</version>
</dependency>

2.log4j.properties(根据具体输出到哪里而定,网上可搜)

#############
# 输出到控制台
#############

# log4j.rootLogger日志输出类别和级别:只输出不低于该级别的日志信息DEBUG < INFO < WARN < ERROR < FATAL
# WARN:日志级别     CONSOLE:输出位置自己定义的一个名字       logfile:输出位置自己定义的一个名字
log4j.rootLogger=WARN,CONSOLE,logfile
# 配置CONSOLE输出到控制台
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 
# 配置CONSOLE设置为自定义布局模式
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout 
# 配置CONSOLE日志的输出格式  [frame] 2019-08-22 22:52:12,000  %r耗费毫秒数 %p日志的优先级 %t线程名 %C所属类名通常为全类名 %L代码中的行号 %x线程相关联的NDC %m日志 %n换行
log4j.appender.CONSOLE.layout.ConversionPattern=[frame] %d{yyyy-MM-dd HH:mm:ss,SSS} - %-4r %-5p [%t] %C:%L %x - %m%n

################
# 输出到日志文件中
################

# 配置logfile输出到文件中 文件大小到达指定尺寸的时候产生新的日志文件
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
# 保存编码格式
log4j.appender.logfile.Encoding=UTF-8
# 输出文件位置此为项目根目录下的logs文件夹中
log4j.appender.logfile.File=logs/root.log
# 后缀可以是KB,MB,GB达到该大小后创建新的日志文件
log4j.appender.logfile.MaxFileSize=10MB
# 设置滚定文件的最大值3 指可以产生root.log.1、root.log.2、root.log.3和root.log四个日志文件
log4j.appender.logfile.MaxBackupIndex=3  
# 配置logfile为自定义布局模式
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n

##########################
# 对不同的类输出不同的日志文件
##########################

# club.bagedate包下的日志单独输出
log4j.logger.club.bagedate=DEBUG,bagedate
# 设置为false该日志信息就不会加入到rootLogger中了
log4j.additivity.club.bagedate=false
# 下面就和上面配置一样了
log4j.appender.bagedate=org.apache.log4j.RollingFileAppender
log4j.appender.bagedate.Encoding=UTF-8
log4j.appender.bagedate.File=logs/bagedate.log
log4j.appender.bagedate.MaxFileSize=10MB
log4j.appender.bagedate.MaxBackupIndex=3
log4j.appender.bagedate.layout=org.apache.log4j.PatternLayout
log4j.appender.bagedate.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n

3.在核心配置文件中配置log4j日志的实现

<settings>
    <!--log4j日志工厂实现-->
    <setting name="logImpl" value="LOG4J">
</setting></settings>

简单使用:

1.在要使用log4j的类中,导入import.org.apache.log4j.Logger;

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

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

3.日志级别

logger.info("info:");
logger.debug("debug:");
logger.error("error:");

10.分页

10.1使用limit分页

语法:select * from xxx表 limit startIndex,pageSize;
select * from user limit 3;//显示前三条

使用mybatis实现分页,核心sql

1.接口

List<user> getUserByLimit(Map<string,integer> map);

2.Mapper.xml

<select id="getUserByLimit" parametertype="map" resultmap="UserMap">
	select * from user limit #{startIndex},#{pageSize}
</select>

3.测试

@Test
public void getUserByLimit() throws IOException {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    HashMap<string, integer=""> map = new HashMap<string, integer="">();
    map.put("startIndex",1);
    map.put("pageSize",2);

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

10.2使用分页插件

pageHelper

11.使用注解开发

11.1基本流程

1.注解在接口上实现(增删改查)

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

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

<mappers>
	<mapper class="com.iweb.domain.UserMapper">
</mapper></mappers>

本质:反射机制实现

底层流程:

1.Resources获取加载全局配置文件

//例如:全局配置文件mybatis-config.xml
InputStream in = Resource.getResourceAsStream("mybatis-config.xml");

2.实例化SqlSessionFactoryBuilder

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

3.解析配置文件流XMLConfigBuilder(SqlSessionFactoryBuilder源码中)

build方法做的事情(3,4步骤)

Mybatis

4.所有的配置信息放在Configuration对象里

Mybatis

5.SqlSessionFactory实例化

(build完之后产生SqlSessionFactory对象)

sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);

6.transaction事务管理

7.创建executor执行器

​ 执行mapper

8.创建sqlSession对象

​ 实现CRUD操作,可能会出现sql问题,事务会回滚,回到步骤6

9.执行成功,提交事务

10.关闭sqlSession

11.2CRUD

1.当方法存在多个参数,所有的参数前必须加上@Param("字段名")这个注解

编写接口,添加注解

@Select("select * from user where id = #{id} and name = #{张三}")
//此时取的参数对应的是注解Param中的参数名,必须和字段名一致
User getUser(@Param("id") int id,@Param("name") String name);

@Select、@Insert、@Update、@Delete

注意:必须要将接口注册绑定到核心配置文件中

关于@Param()注解

基本类型或String类型参数,需要加上,引用类型不需要

如果只有一个基本类型,可以忽略,但建议都加上

在sql中引用的就是这里的@Param("xxx")中设定的属性名,所有要求字段名和属性名相对应

12.多对一处理

Mybatis

多个学生,对应一个老师

对于学生来说,多个学生,(association)关联一个老师【多对一】

对应老师来说,一个老师,(collection)有很多学生【一对多】

环境搭建流程:

1.导入lombok,建立实体类

2.建立对应实体类的Mapper接口

3.建立对应Mapper.xml文件

4.在核心配置文件中绑定注册Mapper接口或文件

5.测试CRUD是否能成功

学生实体类:

@Data
public class Student{
	private int id;
	private String name;
	//学生需要关联一个老师
	private Teacher teacher;
}

老师实体类:

@Data
public class Teacher{
	private int id;
	private String name;
}

查询嵌套处理:

比如,有一个学生表,一个老师表,学生和老师通过tid和id进行关联,

不使用这种形式

select s.id,s.name,t.name from student s,teacher t where s.tid=t.id

而使用嵌套查询

<!--
     思路:
        1. 查询所有的学生信息
        2. 根据查询出来的学生的tid寻找特定的老师 (子查询)
	由于学生信息里有老师这个对象,所以需要单独处理
    -->
<select id="getStudent" resultmap="StudentTeacher">
    select * from student
</select>

<resultmap id="StudentTeacher" type="student">
    <result property="id" column="id">
    <result property="name" column="name">
    <!--复杂的属性,我们需要单独出来 对象:association 集合:collection
	javaType:由于是一个对象,需要给对象设置类型
	-->
    <collection property="teacher" column="tid" javatype="teacher" select="getTeacher"> <!--这里的select就是嵌套查询-->
</collection></result></result></resultmap>

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

按照结果嵌套查询:

<!--按照结果进行查询-->
<select id="getStudent2" resultmap="StudentTeacher2">
    select s.id sid , s.name sname, t.name tname
    from student s,teacher t
    where s.tid=t.id
</select>
<!--结果封装,将查询出来的列封装到对象属性中-->
<resultmap id="StudentTeacher2" type="student">
    <result property="id" column="sid">
    <result property="name" column="sname">
    
    <association property="teacher" javatype="teacher">
        <result property="name" column="tname"></result>
    </association>
</result></result></resultmap>

13.一对多处理

学生实体类:

@Data
public class Student{
	private int id;
	private String name;
	private int tid;
}

老师实体类:

@Data
public class Student{
	private int id;
	private String name;
	//一个老师拥有多个学生
	private List<student> students;
}

按照结果嵌套查询:

<!--按结果嵌套查询-->
<select id="getTeacher" resultmap="StudentTeacher">
    SELECT s.id sid, s.name sname,t.name tname,t.id tid FROM student s, teacher t
    WHERE s.tid = t.id AND tid = #{tid}
</select>
<resultmap id="StudentTeacher" type="Teacher">
    <result property="id" column="tid">
    <result property="name" column="tname">
    <!--复杂的属性,我们需要单独处理 对象:association 集合:collection
    javaType=""指定属性的类型!
    集合中的泛型信息,我们使用ofType获取
    -->
    <collection property="students" oftype="Student">
        <result property="id" column="sid">
        <result property="name" column="sname">
        <result property="tid" column="tid">
    </result></result></result></collection>
</result></result></resultmap>

建议使用结果嵌套查询

小结:

​ javaTpye:用来指定实体类中属性的类型

​ ofType:用来指定映射到List或集合中的pojo类型,泛型中的类型

14.动态sql:

​ 也就是sql语句,在sql语句中,执行一些逻辑代码,比如if,where,choose,when

主要用来解决根据不同条件拼接sql语句,比如拼接时不能忘记添加必要的空格,去掉列表最后一个列名的逗号

14.1 if语句:

​ 固定格式

​ 根据 username 和 sex 来查询数据。如果username为空,那么将只根据sex来查询;反之只根据username来查询

<select id="selectUserByUsernameAndSex" resulttype="user" parametertype="com.iweb.entity.User">
    select * from user where
        
           username=#{username}
        
         
        
           and sex=#{sex}
        
</select>

14.2 if+where 语句:

​ 这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where关键字。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。

比如说,username !=null不成立,执行的sql语句就会变成select * from user and sex=?

加上where标签后,会自动将and剔除

<select id="selectUserByUsernameAndSex" resulttype="user" parametertype="com.iweb.entity.User">
    select * from user
    
        
           username=#{username}
        
         
        
           and sex=#{sex}
        
    
</select>

14.3 if+set 语句

​ set会动态前置SET关键字,同时也会删掉无关的逗号

<!-- 根据 id 更新 user 表的数据 -->
<update id="updateUserById" parametertype="com.iweb.entity.User">
    update user u
        <set>
            <if test="username != null and username != ''">
                u.username = #{username},<!--注意此处的,不要忽略了-->
            </if>
            <if test="sex != null and sex != ''">
                u.sex = #{sex}
            </if>
        </set>
     
     where id=#{id}

这样写,如果第一个条件 username 为空,那么 sql 语句为:update user u set u.sex=? where id=?

如果第一个条件不为空,那么 sql 语句为:update user u set u.username = ? ,u.sex = ? where id=?

14.4 choose(when,otherwise) 语句

​ 有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句

<select id="selectUserByChoose" resulttype="com.iweb.entity.User" parametertype="com.iweb.entity.User">
      select * from user
      
          
              
                  id=#{id}
              
              
                  and username=#{username}
              
              
                  and sex=#{sex}
              
          
      
  </select>

也就是说,这里我们有三个条件,id,username,sex,只能选择一个作为查询条件

如果 id 不为空,那么查询语句为:select * from user where id=?

如果 id 为空,那么看username 是否为空,如果不为空,那么语句为 select * from user where username=?;

如果 username 为空,那么查询语句为 select * from user where sex=?

14.5 trim 语句

​ trim标记是一个格式化的标记,可以完成set或者是where标记的功能。

​ 也就是满足条件就将对应的条件写在sql语句中

​ prefix前缀,prefixOverrides前缀覆盖。

​ suffix后缀,suffixOverrides后缀覆盖。

<select id="selectUserByUsernameAndSex" resulttype="user" parametertype="com.iweb.entity.User">
        select * from user
        <!-- <where>
            <if test="username != null">
               username=#{username}
            </if>
             
            <if test="username != null">
               and sex=#{sex}
            </if>
        </where>  -->
    <!--上下两个所展现的sql语句都是同一个-->
        
            
               and username=#{username}
            
            
               and sex=#{sex}
            
        
    </select>

14.6 SQL 片段

​ 有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。

1.使用sql标签抽取公共的部分

比如:假如我们需要经常根据用户名和性别来进行联合查询,那么我们就把这个代码抽取出来

<!-- 定义 sql 片段 -->
<sql id="selectUserByUserNameAndSexSQL">
    <if test="username != null and username != ''">
        AND username = #{username}
    </if>
    <if test="sex != null and sex != ''">
        AND sex = #{sex}
    </if>
</sql>

2.在需要使用的地方使用include标签引用即可

引用 sql 片段

<select id="selectUserByUsernameAndSex" resulttype="user" parametertype="com.iweb.entity.User">
    select * from user
  	<!-- trim 标记是一个格式化的标记,可以完成set或者是where标记的功能 
    	prefix:前缀      
 		 prefixoverride:去掉第一个and或者是or
    -->
    
        <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
        
        <!-- 在这里还可以引用其他的 sql 片段 -->
    
</select>

注意事项:

1.最好基于单表来定义sql片段,因为多表联合查询sql复杂,再引用可能会导致前面的sql语句不匹配

2.不要存在where标签

14.7 foreach语句

需求:我们需要查询 user 表中 id 分别为1,2,3的用户

sql语句:select * from user where id=1 or id=2 or id=3

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

<!-- 改写select * from user where id=1 or id=2 or id=3  -->
<select id="selectUserByListId" parametertype="list" resulttype="com.iweb.entity.User">
    select * from user
    
        <!--
            collection:指定输入对象中的集合属性
            item:每次遍历生成的对象
            open:开始遍历时的拼接字符串
            close:结束时拼接的字符串
            separator:遍历对象之间需要拼接的字符串
            select * from user where 1=1 and (id=1 or id=2 or id=3)
          -->
        
            id=#{id}
        
    
</select>
<!-- 改写select * from user where id in(1,2,3) -->
<select id="selectUserByListId" parametertype="list" resulttype="com.iweb.entity.User">
        select * from user
        
            <!--
                collection:指定输入对象中的集合属性
                item:每次遍历生成的对象
                open:开始遍历时的拼接字符串
                close:结束时拼接的字符串
                separator:遍历对象之间需要拼接的字符串
                select * from user where 1=1 and id in (1,2,3)
              -->
            
                #{id}
            
        
    </select>

小结:

动态sql就是在拼接sql语句,我们需要保证sql的正确性,按照sql的格式,组合在一起就可以了

建议:先写出完整的sql,再对应去修改成动态sql实现通用

15.缓存

15.1简介

1.什么是缓存[Cache]?

存在内存中的临时数据
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题

2.为什么使用缓存?

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

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

经常查询并且不经常改变的数据 【可以使用缓存】

15.2mybatis缓存

MyBatis系统中默认定义了两级缓存:一级缓存二级缓存

  • 默认情况下,只有一级缓存开启(SqlSession级别的缓存,也称为本地缓存)

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

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

15.3一级缓存

一级缓存也叫本地缓存:SqlSession

缓存只在一个事务中有效,即同一个事务中先后执行多次同一个查询,只在第一次真正查询数据库,并将结果缓存,之后的查询都直接 获取缓存中的数据,如果是不同的事务,则缓存是隔离的

mybatis一级缓存默认又称为session级缓存,默认就是开启状态,且无法手动关闭

注意,在同一个事务中,如果发生了的修改更新操作(也就是增删改),一级缓存会失效

缓存失效的情况:

  1. 查询不同的东西
  2. 增删改操作,可能会改变原来的数据,所以必定会刷新缓存
  3. 查询不同的Mapper.xml
  4. 手动清理缓存
SqlSession.clearCache();

15.4二级缓存

mybatis的二级缓存默认是关闭的,需手动开启,且默认情况下,如果事务没有提交,二级缓存是失效的。另外,想要被二级缓存缓存的对象必须实现序列化结果

  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
  • 工作机制
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
    • 如果会话关闭了,这个会员对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中
    • 新的会话查询信息,就可以从二级缓存中获取内容
    • 不同的mapper查询出的数据会放在自己对应的缓存(map)中

缓存全局有效,一个事务查询的一个sql得到结果,会被缓存起来,之后只要缓存未被清除,则其他事务如果查询同一个sql,得到的将会是之前缓存的结果,二级缓存作用范围大,作用时间长,可能造成的危害也更大,所以在开发中一般很少启用二级缓存

二级缓存又称为SqlSessionFactory级缓存,可以在不同的session间缓存

当启用了二级缓存时,mybatis的查询顺序是,二级缓存-->一级缓存-->数据库

手动开启二级缓存:

主配置文件

<!--开启二级缓存 总开关 -->
    <settings>
        <setting name="cacheEnabled" value="true">
    </setting></settings>

mapper.xml

 <cache>

举例:

Mybatis

注意:只有查询才有缓存,根据数据是否需要缓存(修改是否频繁选择是否开启)useCache=“true”

 <select id="getUserById" resulttype="user" usecache="true">
        select * from user where id = #{id}
    </select>

小结:

1.只要开启了二级缓存,同一个Mapper下就有效

2.所有的数据都会先放在一级缓存中

3.只要当会话提交,或者关闭的时候,才会提交到二级缓存中

4.当启用了二级缓存时,mybatis的查询顺序是,二级缓存-->一级缓存-->数据库

上一篇:Spring Boot (日志篇):Log4j整合ELK,搭建实时日志平台


下一篇:log4j日志