Mybatis

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

1、Mybatis

1.1 什么是 MyBatis?

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

中文文档地址:https://mybatis.org/mybatis-3/zh/getting-started.html

1.2 持久化

数据持久化

  • 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
  • 内存:断电即失

1.3 持久层

DAO层 Service层 Controller层

  • 完成持久化工作的代码
  • 层之间的界限是否明显

1.4 为什么需要Mybatis

简化JDBC框架和代码,自动化

  • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
  • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
  • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
  • 提供映射标签,支持对象与数据库的orm字段关系映射
  • 提供对象关系映射标签,支持对象关系组建维护
  • 提供xml标签,支持编写动态sql。

2、第一个Mybatis程序

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

1.创建数据库

2.导入mybatis

至少要导入三个包

junit

mysql-connector-java

mybatis

3.编写mybatis配置文件

这个部分是用于创建SqlsessionFactory

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="User1"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

</configuration>

4.编写pojo和dao层和测试类

开启maven资源过滤 不然可能搜不到

<resources>
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.properties</include>
            <include>**/*.xml</include>
        </includes>
        <filtering>true</filtering>
    </resource>
    <resource>
        <directory>src/main/resources</directory>
        <includes>
            <include>**/*.properties</include>
            <include>**/*.xml</include>
        </includes>
        <filtering>true</filtering>
    </resource>
</resources>

pojo

package com.cx.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//lombok的三个标签  分别是set,get 无参构造 有参构造
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}

dao

package com.cx.dao;

import com.cx.pojo.User;

import java.util.List;


public interface UserMapper {
    public List<User> selectAllUser();
}
<?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.cx.dao.UserMapper">
    <select id="selectAllUser" resultType="com.cx.pojo.User">
        select * from users
    </select>
</mapper>

在mybatis配置中设置mappers

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

Test类

import com.cx.dao.UserMapper;
import com.cx.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;


public class MybatisTest {

    @Test
    public void TestMysql() throws IOException {
        String resource = "mybatis.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        try (SqlSession session = sqlSessionFactory.openSession()) {
            UserMapper userMapper = session.getMapper(UserMapper.class);
            List<User> users = userMapper.selectAllUser();
            for(User user : users){
                System.out.println(user.toString());
            }
            session.close();
        }
    }
}

除了mapper.xml方式 你也可以使用java方式执行sql

3、CRUD

1.namespace

对命名空间的一点补充

在之前版本的 MyBatis 中,**命名空间(Namespaces)**的作用并不大,是可选的。 但现在,随着命名空间越发重要,你必须指定命名空间。

命名空间的作用有两个,一个是利用更长的全限定名来将不同的语句隔离开来,同时也实现了你上面见到的接口绑定 。就算你觉得暂时用不到接口绑定,你也应该遵循这里的规定,以防哪天你改变了主意。 长远来看,只要将命名空间置于合适的 Java 包命名空间之中,你的代码会变得更加整洁,也有利于你更方便地使用 MyBatis。

**命名解析:**为了减少输入量,MyBatis 对所有具有名称的配置元素(包括语句,结果映射,缓存等)使用了如下的命名解析规则。

  • 全限定名(比如 “com.mypackage.MyMapper.selectAllThings)将被直接用于查找及使用。
  • 短名称(比如 “selectAllThings”)如果全局唯一也可以作为一个单独的引用。 如果不唯一,有两个或两个以上的相同名称(比如 “com.foo.selectAllThings” 和 “com.bar.selectAllThings”),那么使用时就会产生“短名称不唯一”的错误,这种情况下就必须使用全限定名。

2.增删改查

<?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.cx.dao.UserMapper">
    <select id="selectAllUser" resultType="com.cx.pojo.User">
        select * from users
    </select>

    <select id="selectUserById" parameterType="Integer" resultType="com.cx.pojo.User">
        select * from users where id=#{id}
    </select>

    <delete id="deleteUserById" parameterType="Integer">
        delete from users
        while id = #{id}
    </delete>
    <insert id="addUser" parameterType="com.cx.pojo.User">
        insert into users(id,name,pwd) values(#{id},#{name},#{pwd})
    </insert>
    <update id="updataUserById" parameterType="com.cx.pojo.User">
        update users set name = #{name},pwd=#{pwd} where id=#{id}
    </update>
    <!--对象里面的属性能直接取出来-->
</mapper>
import com.cx.dao.UserMapper;
import com.cx.pojo.User;
import com.cx.utils.MybatisUntils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;


public class MybatisTest {

    @Test
    public void TestMysql() throws IOException {
        String resource = "mybatis.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//build里面自己关闭了文件流

        try (SqlSession session = sqlSessionFactory.openSession()) {
            UserMapper userMapper = session.getMapper(UserMapper.class);
            List<User> users = userMapper.selectAllUser();
            for(User user : users){
                System.out.println(user.toString());
            }
            session.close();
        }
    }

    @Test
    public void TestMysqlGet() throws IOException {
        SqlSession session = MybatisUntils.getSqlSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        User user = userMapper.selectUserById(1);
        System.out.println(user.toString());
        session.close();
    }

    //增删改需要增加事务
    @Test
    public void TestMysqlAdd() throws IOException {
        SqlSession session = MybatisUntils.getSqlSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        userMapper.addUser(new User(4,"k","123456"));
        session.commit();//提交事务
        session.close();
    }

    @Test
    public void TestMysqlDel() throws IOException {
        SqlSession session = MybatisUntils.getSqlSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        userMapper.deleteUserById(1);
        session.commit();//提交事务
        session.close();//一定要记得关闭
    }

    @Test
    public void TestMysqlUpdate() throws IOException {
        SqlSession session = MybatisUntils.getSqlSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        userMapper.updataUserById(new User(1,"k8","123456"));
        session.commit();//提交事务
        List<User> users = userMapper.selectAllUser();
        for(User user : users){
            System.out.println(user.toString());
        }
        session.close();//一定要记得关闭
    }
}
package com.cx.dao;

import com.cx.pojo.User;

import java.util.List;


public interface UserMapper {
    public List<User> selectAllUser();
    public User selectUserById(Integer id);

    public void deleteUserById(Integer id);

    public void updataUserById(User user);

    public void addUser(User user);
}

3.模糊查询

  • and
  • like 在java代码里面加上通配符可能会有sql注入,在xml里面写%

4、配置解析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PXt5hH2U-1619418205061)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210414154008316.png)]

1.属性(properties)用于数据源,用于动态配置

datasouce里面的动态属性

<dataSource type="POOLED">
  <property name="driver" value="${driver}"/>
  <property name="url" value="${url}"/>
  <property name="username" value="${username}"/>
  <property name="password" value="${password}"/>
</dataSource>
<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

配置属性,有放在另外一个文件中,供用户修改,其中的resource用于引入配置文件

xml中标签中可以设定顺序要求,比如properties标签必须放在第一个

在xml中&写成& amp;,但是properties文件中直接写做&

有三种设置方式设置properties,但有优先级顺序

  • 首先读取在 properties 元素体内指定的属性。
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
  • 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。java方式,sqlsessionfactory.build

2.设置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mpF0pYQE-1619418205064)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210414165825330.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RiZ5gElY-1619418205065)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210414165849109.png)]

3.别名

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

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

<!-- 指定别名,用包的方式   -->
    <typeAliases>
        <package name="com.cx"/>
    </typeAliases>

在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 com.cx.User 的别名为 user;若有注解,则别名为其注解值。见下面的例子:

@Alias("author")
public class Author {
    ...
}

mybatis已经设置了一些固定的别名比如map

4.环境(environment)

一个环境用于一个SqlsessionFactory

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QtLvbiXV-1619418205066)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210414162629844.png)]

在SqlsessionFactoryBuilder里面的.build可以选择环境

如果忽略了环境参数,那么将会加载默认环境,如下所示:

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H6TGXVyr-1619418205067)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210414162656430.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ttGusRiX-1619418205068)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210414162827807.png)]

数据池:用完可以回收,一个链接用完之后本来是要被抛弃关闭的,但是可以放到数据池中,等待另外一个人来链接,默认的为pooled

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kGszuaVA-1619418205069)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210414163000182.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CNczItKH-1619418205069)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210414163013363.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MxZyuNkQ-1619418205070)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210414163025106.png)]

5.mappers映射器

<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>后面两种需要java类和xml同名 比如UserMapper.java对应的必须是UserMapper.xml且都要在同一个包下

5、生命周期和作用域

理解我们之前讨论过的不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题


提示 对象生命周期和依赖注入框架

依赖注入框架可以创建线程安全的、基于事务的 SqlSession 和映射器,并将它们直接注入到你的 bean 中,因此可以直接忽略它们的生命周期。 如果对如何通过依赖注入框架使用 MyBatis 感兴趣,可以研究一下 MyBatis-Spring 或 MyBatis-Guice 两个子项目。


SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式

SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:

try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的应用逻辑代码
}

在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。

映射器实例

映射器是一些绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的。虽然从技术层面上来讲,任何映射器实例的最大作用域与请求它们的 SqlSession 相同。但方法作用域才是映射器实例的最合适的作用域。 也就是说,映射器实例应该在调用它们的方法中被获取,使用完毕之后即可丢弃映射器实例并不需要被显式地关闭。尽管在整个请求作用域保留映射器实例不会有什么问题,但是你很快会发现,在这个作用域上管理太多像 SqlSession 的资源会让你忙不过来。 因此,最好将映射器放在方法作用域内。就像下面的例子一样:

try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  // 你的应用逻辑代码
}

6、XML映射器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Me5NslLZ-1619418205070)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210414171850883.png)]

6.1 resultMap 用于解决数据库和pojo类属性不同

<resultMap id="usermap" type="User">
    <id column="pwd" property="password" ></id>
</resultMap>

resultMap属性只有select语句里面才有

column是数据库中的字段,property是实体类中的属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ow6OB1Ae-1619418205071)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210420155236401.png)]

7、日志工厂

<configuration>
  <settings>
    ...
    <setting name="logImpl" value="LOG4J"/>#不能随便写位置  不能随便写,多一个空格你可能都找不到错误
    ...
  </settings>
</configuration>
      

可选的值有:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING(默认的选择,无需配置)、NO_LOGGING,或者是实现了 org.apache.ibatis.logging.Log 接口,且构造方法以字符串为参数的类完全限定名。

log4j(需要导入包和对log4j进行配置)

Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
### 设置###
log4j.rootLogger=error,stdout,D
###设置Mapper映射器的日志级别为TRACE###
log4j.logger.com.cx.dao=TRACE
### 输出信息到控制抬 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
#### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = ./log/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
#
#### 输出ERROR 级别以上的日志到=E://logs/error.log ###
#log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.E.File =E://logs/error.log
#log4j.appender.E.Append = true
#log4j.appender.E.Threshold = ERROR
#log4j.appender.E.layout = org.apache.log4j.PatternLayout
#log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

在java中使用log4j

static Logger log4jTest = Logger.getLogger(MybatisTest.class);

在方法中使用log4jTest.info()等输出日志级别

8、分页

8.1 limit分页

SELECT * from users limit startindex,pagesize;
SELECT * from users limit startindex&pagesize;只给一个值代表两个参数都是一样的

usermapper.java

public List<User> getUserByLimit(Map<String,Integer> map);

usermapper.xml

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

test

    @Test
    public void TestMysqlGetByLimit() throws IOException {
        SqlSession session = MybatisUntils.getSqlSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        Map<String,Integer> map = new HashMap<String,Integer>();
        map.put("startIndex",0);
        map.put("pageSize",2);
        List<User> users = userMapper.getUserByLimit(map);
        for(User user : users){
            System.out.println(user.toString());
        }
        session.close();
    }

8.2、RowBounds(比较老)

@Test
public void TestMysqlGetByRowBounds() throws IOException {
    SqlSession session = MybatisUntils.getSqlSession();
    //RowBounds
    RowBounds r = new RowBounds(1,2);
    List<User> users = session.selectList("com.cx.dao.UserMapper.getUserByRowBounds",null,r);
    for(User user : users){
        System.out.println(user.toString());
    }
    session.close();
}

8.3、分页插件

你可以通过插件和配置来实现分页比如使用mybatis.pagehelper

9、使用注解开发

9.1、面向接口编程

在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。

1.关于接口的理解。

接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。

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

接口应有两类:第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);

第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);

一个体有可能有多个抽象面。

抽象体与抽象面是有区别的。

2.设计接口的另一个不可忽视的因素是接口所处的环境(context,environment),系统论的观点:环境是系统要素所处的空间与外部影响因素的总和。任何接口都是在一定的环境中产生的。因此环境的定义及环境的变化对接口的影响是不容忽视的,脱离原先的环境,所有的接口将失去原有的意义。

3.按照组件的开发模型(3C),它们三者相辅相成,各司一面,浑然一体,缺一不可。

面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法

面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现

接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题

9.2 注解

注解开发的核心是使用反射机制来获取方法的参数,返回类型等

绑定mapper

<!--mapper文件扫描注册-->
    <mappers>
        <package name="com.cx.dao"/>
    </mappers>
public interface UserMapper {
    /* sss*/
    @Select("Select * from users where 1")
    List<User> selectAllUser();

    /* 方法存在多个参数时,必须加上@Param */
    @Select("Select * from users where id=#{id2}")
    User selectById(@Param("id2") Integer id);

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

    @Delete("delete from users where id=#{id}")
    int delUser(Integer id);
}

复杂的sql难以使用注解实现

其他del,update,insert等注解的方式相差不大

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wcOCtTcp-1619418205071)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210420211556291.png)]

在获取sqlsession时可以实现自动提交事务

public class MybatisUntils {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try{
            String resource = "mybatis.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession(true);//设置自动提交事务
    }
}

关于@Param注解

  • 基本类型的参数或者String类型需要叫上
  • 引用类型不需要加
  • 如果只有一个基本类型的话,可以忽略但是建议加上
  • 我们在sql中引用的就是@Param中设定的属性名

@Param注解来声明参数时,如果使用 #{} 或 ${} 的方式都可以,当你不使用@Param注解来声明参数时,必须使用使用 #{}方式。如果使用另一种的方式,会报错,但是第二种方式会导致sql注入,所以一般只在使用动态sql的时候使用。

10、Lombok

Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.

//lombok的三个标签  分别是set,get 无参构造 有参构造
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int id;
    private String name;
    private String password;
}
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <!--            <optional>true</optional>-->
    <vision></vision>
</dependency>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s7mPNvAk-1619418205072)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210420214120753.png)]

idea上使用需要安装lombok插件

11、多对一和一对多

搭建环境,数据库,student teacher表 pojo dao

多对一

Students里面包含teacher对象,如何进行查询?

根据数据库里面存储的tid查询老师的id,查出对应老师的信息

1.使用子查询进行嵌套处理

    <resultMap id="stuMap" type="Student">
        <id column="id" property="id"></id>
        <result column="name" property="name"></result>
<!--        复杂的属性我们需要单独处理
            对象使用association 
            集合使用collection
-->
        <association property="teaher" column="tid" javaType="Teacher" select="getTeacher">
            <result column="id" property="id"></result>
            <result column="name" property="name"></result>
        </association>
    </resultMap>
    <select id="getTeacher" resultType="Teacher">
        select * from teacher where id=#{tid}//这里最好对应上面的column
    </select>
	<select id="getStudentInfo" resultType="Student" resultMap="stuMap">
        select * from student
    </select>

2.根据结果嵌套查询

<resultMap id="stuMap2" type="Student">
    <result property="id" column="sid"/>
    <result property="name" column="sname"/>
    <association property="teacher" javaType="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
    </association>
</resultMap>
<select id="getStudentInfo2" resultMap="stuMap2">
    select s.id sid,s.name sname,t.name tname,t.id tid
    from student s,teacher t
    where s.tid=tid
</select>

一对多

teacher里面有多个学生对象,如何进行查询?

根据老师的id查询学生表里面的tid,相应的学生放到老师的集合中

1.按结果嵌套查询

<resultMap id="TtoSMap" type="Teacher">
    <result column="tid" property="id"/>
    <result property="name" column="tname"/>
    <collection property="students" ofType="Student">//javaType=ArrayList ofType是你集合里面的类型
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tid" column="tid"/>
    </collection>
</resultMap>
<select id="getTeacherById" resultMap="TtoSMap" parameterType="Integer">
    select t.id tid, t.name tname,s.id sid,s.name sname
    from teacher t,student s
    where t.id=s.tid and t.id=#{teacherid}
</select>
<select id="getTeacherInfo" resultMap="TtoSMap">
    select t.id tid, t.name tname,s.id sid,s.name sname
    from teacher t,student s
    where t.id=s.tid
</select>

2.子查询

<resultMap id="TtoSMap2" type="Teacher">
    <collection property="students" ofType="Student" column="id" select="getTSById" />//使用column传递的参数
</resultMap>
<select id="getTSById" resultType="Student">
    select *
    from Student
    where tid = #{teacherid}
</select>
<select id="getTeacherById2" resultMap="TtoSMap2" parameterType="Integer">
    select *
    from teacher
    where id = #{teacherid}
</select>

关联【association】 多对一

集合【collection】 一对多

javaType 指定实体类中的属性类型

ofType 指定集合中的属性类型

注意点:

  • 保证sql的可读性,尽量保证通俗易懂
  • 注意一对多和多对一中属性名和字段的问题
  • 使用日志可以更好的排除错误
  • sql可以使用得更好一点,比如加索引

12、动态SQL

什么是动态sql,就是指根据不同的条件生成不同的sql语句

Mybatis的动态sql只有以下标签

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rRmMowGT-1619418205072)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210421161325464.png)]

if标签

  <if test="title != null">
    AND title like #{title}
  </if>

只有在满足if条件时才会插入这个条件

choose when otherwise

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

还是上面的例子,但是策略变为:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG(这可能是管理员认为,与其返回大量的无意义随机 Blog,还不如返回一些由管理员精选的 Blog)。

where

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

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <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>
</select>

Set

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">//separat也可以是其他符号,比如or
        #{item}
  </foreach>
</select>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

script

要在带注解的映射器接口类中使用动态 SQL,可以使用 script 元素。比如:

    @Update({"<script>",
      "update Author",
      "  <set>",
      "    <if test='username != null'>username=#{username},</if>",
      "    <if test='password != null'>password=#{password},</if>",
      "    <if test='email != null'>email=#{email},</if>",
      "    <if test='bio != null'>bio=#{bio}</if>",
      "  </set>",
      "where id=#{id}",
      "</script>"})
    void updateAuthorValues(Author author);

多数据库支持

如果配置了 databaseIdProvider,你就可以在动态代码中使用名为 “_databaseId” 的变量来为不同的数据库构建特定的语句。比如下面的例子:

<insert id="insert">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    <if test="_databaseId == 'oracle'">
      select seq_users.nextval from dual
    </if>
    <if test="_databaseId == 'db2'">
      select nextval for seq_users from sysibm.sysdummy1"
    </if>
  </selectKey>
  insert into users values (#{id}, #{name})
</insert>

bind

bind 元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。比如:

<select id="selectBlogsLike" resultType="Blog">
  <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
  SELECT * FROM BLOG
  WHERE title LIKE #{pattern}
</select>

13、缓存

查询连接数据库会消耗资源,缓存就是把查询到的结果放到内存中。

再次查询相同数据时,就直接取出来就行。

用来解决系统的高并发性能问题

Mybatis具有一个非常强大的查询缓存特性,具有一级缓存和二级缓存,跨域非常方便的定制和配置。

一般只开启了一级缓存,及sqlsession的缓存,只在sqlsession的作用域中存在,在sqlsession关闭时,一级缓存就消失了

二级缓存也称全局缓存

要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

<cache/>

基本上就是这样。这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

工作机制:

  • 一个会话查询一条数据,这个数据就会被放到当前会话的一级缓存中
  • 当前会话关闭时,一级缓存中的数据会被保存到二级缓存中
  • 新的会话会先到二级缓存中查找
  • 不同的mapper有不同的二级缓存map<sql语句加上其他符号,查询出来的值>

步骤:

  1. 开启全局缓存

    <settings>
    <!--        显示地开启全局缓存-->
            <setting name="cacheEnabled" value="true"/>
    </settings>
    
  2. 在mapper.xml中开启二级缓存

     <cache
         eviction="FIFO"
         flushInterval="60000"
         size="512"
         readOnly="true"/>
    可用的清除策略(eviction)有:
    LRU – 最近最少使用:移除最长时间不被使用的对象。
    FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
    SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
    WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
    
  3. pojo类需要满足序列化接口

    public class Blog implements Serializable 
    

img

面试高频与进阶

  • mysql引擎
  • InnoDB的原理
  • 索引
    缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

工作机制:

  • 一个会话查询一条数据,这个数据就会被放到当前会话的一级缓存中
  • 当前会话关闭时,一级缓存中的数据会被保存到二级缓存中
  • 新的会话会先到二级缓存中查找
  • 不同的mapper有不同的二级缓存map<sql语句加上其他符号,查询出来的值>

步骤:

  1. 开启全局缓存

    <settings>
    <!--        显示地开启全局缓存-->
            <setting name="cacheEnabled" value="true"/>
    </settings>
    
  2. 在mapper.xml中开启二级缓存

     <cache
         eviction="FIFO"
         flushInterval="60000"
         size="512"
         readOnly="true"/>
    可用的清除策略(eviction)有:
    LRU – 最近最少使用:移除最长时间不被使用的对象。
    FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
    SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
    WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
    
  3. pojo类需要满足序列化接口

    public class Blog implements Serializable 
    

[外链图片转存中…(img-Zab5dfgS-1619418205073)]

面试高频与进阶

  • mysql引擎
  • InnoDB的原理
  • 索引
  • 优化索引
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值