美文网首页
Mybatis-2-1.SqlSession和Mapper

Mybatis-2-1.SqlSession和Mapper

作者: Blacol | 来源:发表于2022-04-27 23:34 被阅读0次

Mybatis基本工作原理

工作原理

(1)读取MyBatis的配置文件。mybatis-config.xml为MyBatis的全局配置文件,用于配置数据库连接信息。

(2)加载映射文件。映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在MyBatis配置文件mybatis-config.xml中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。

(3)构造会话工厂。通过MyBatis的环境配置信息构建会话工厂SqlSessionFactory。

(4)创建会话对象。由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有方法。

(5)Executor执行器。MyBatis底层定义了一个Executor接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。

(6)MappedStatement对象。在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。

(7)输入参数映射。输入参数类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输入参数映射过程类似于JDBC对preparedStatement对象设置参数的过程。

(8)输出结果映射。输出结果类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输出结果映射过程类似于JDBC对结果集的解析过程。

SqlSessionFactoryBuilder、SqlSessionFactory

SqlSessionFactoryBuilder用于构建SqlSessionFactory。这是基于建造者模式而设计的。
SqlSessionFactoryBuilder可以通过InputStream或者Reader获取mybatis的配置文件中的内容自动配置SqlSessionFactory。SqlSessionFactory用来创造SqlSession。而Mybatis的使用就是从SqlSession开始的。

SqlSession

是Sql数据库会话,想要执行Mapper中的方法就需要用到SqlSession。
SqlSession有单例的和非单例的。在与框架整合后,SqlSession是单例的。但没有整合的时候可以是单例的也可以不是。
之所以使用单例SqlSession是因为SqlSession根本没必要有多个。按照SqlSession的生命中期,它在执行完一条sql语句之后就应该结束生命周期。这么做的话并不利于提高开发效率(每执行完一条语句都要写一个session.close()效率太低。),所以直接用单例,这样它将贯穿整个程序的运行周期而不需要关闭它,大幅度提高开发效率。

Mapper以及Mapper映射文件

Mapper是一个接口。这里面封装所有要执行的方法。在程序运行前,Mybatis会将Mapper映射文件和Mapper接口做关联。举例:

@Mapper//告知系统该类是一个Mapper类,只有拥有该注解的类才能被当做Mapper使用
public interface UserMapper{
  List<User> selectAllUsers();
  User selectUserById(String id);
}

上面这串代码中的selectAllUsers是获取全部User的方法。该方法有多条结果返回所以返回值是List,而返回的数据是User类型的,所以List的泛型是User。selectUserById是通过ID获取User的方法。因为只有一条记录返回(假设id是User表的主键)所以使用User即可。
当然,只写这些方法是不会有任何作用的。必须还要有对应的Mapper映射文件。

Mapper映射文件和Mapper是一一对应的。Mapper中的方法和Mapper映射文件中的方法也是对应的。
下面是UserMapper配置文件的例子(文件名叫做UserMapper.xml,Mapper映射文件的文件名最好和类名一致):(这里会给出xml文件头部内容,各位读者可以复制头部内容供以后使用。为了方便,后续的章节中所有xml文件的示例代码都不再写头部内容。)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.blacol.UserMapper">
  <select id="selectAllUsers" resultType="com.blacol.User"><!--必须要有resultType或者resultMap属性-->
    <!--id要和方法名一致,resultType的值要和返回值或者泛型一致-->
    select * from user
  </select>
  <select id="selectUserById" resultType="com.blacol.User">
    select * from user where id=#{id}
  </select>
</mapper>

这样当调用UserMapper下的selectAllUsers的时候Mybatis就会执行UserMapper.xml文件中id为selectAllUsers的sql语句。

下面介绍一下Mapper映射文件的基本内容:
Mapper映射文件是由<mapper>根标签组成的。在根标签中可以书写<select><insert><update><delete>标签来对应执行不同的操作。select就是获取,insert就是插入,update就是更新,delete就是删除。
所有标签必须有一个id属性,而select标签必须有resultType或者resultMap属性。(两者选择一个)。insert标签有parameterType属性用于指定参数类型,大多数情况下并不需要该属性。
除了这些还有<resultMap>等标签也是可以写的。这部分内容等到后面的关联查询部分会详细讲解。

细心的读者可能会发现Mapper方法中带有参数怎么传递给Mapper映射文件呢?在上面的例子已经给出了答案,在selectUserById这个sql语句中有#{id}。它用来将参数id的值放入到sql语句中。类似的还有${},但是这个不常用。两者之间是有区别的:
${}不会预编译,参数是什么拿过来就直接用了。即参数在sql语句中是这个样子的:... where name=张三,当sql语句执行时就会报错。同时这个符号不会阻止sql注入(一种攻击方法,它的危害可以在百度上查找)。而#{}就会预编译,现将参数预编译成?然后将数据自动填入?中,这样参数在sql语句中就变成了... where name=?,填入参数后变为... where name='张三'。因此#{}可以防止sql注入。(类似于java连接数据库时的PreparedStatement

实操-商品信息的CRUD(增删改查)

  1. 创建数据库
    我使用DBeaver创建的数据库,使用Navicate也可以。


    step1-创建数据库

(我用的sql是8版本的,如果是5版本,字符集是utf8,排序规则是utf8_general_ci)

  1. 创建数据表
CREATE TABLE `Product` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`price` double(10,2) DEFAULT NULL,
PRIMARY KEY (`id`)
) ;
  1. 创建普通Maven项目


    step3.png

    创建项目的过程省略。

  2. 引入Mybatis依赖并更新maven

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.9</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.28</version>
    </dependency>
    
  3. 创建软件包并创建DBUtil类。包名自拟

    public class DBUtil {
        private static SqlSession session;
        private static void buildSqlSession() throws IOException {
            //单例SqlSession
            SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
            InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-cfg.xml");
            SqlSessionFactory build = builder.build(resourceAsStream);
            session=build.openSession();
        }
        public static SqlSession getSession() throws IOException {
            buildSqlSession();
            return session;
        }
    }
    

    这是单例SqlSession,你也可以创建不是单例的SqlSession。这里以单例的SqlSession为例。

  4. 创建实体类、Mapper和Mapper映射文件

    • 实体类-Product
      public class Product {
        private long id;
        private String name;
        private double price;
        //为了方便,以后Getter和Setter的代码将省略不写同时通过注释标记出该类有Getter和Setter
        public long getId() {
          return id;
        }
        public void setId(long id) {
          this.id = id;
        }
        public String getName() {
          return name;
        }
        public void setName(String name) {
          this.name = name;
        }
        public double getPrice() {
          return price;
        }
        public void setPrice(double price) {
          this.price = price;
        }
      }
      
    • Mapper-ProductMapper
      @Mapper
      public interface ProductMapper {
          List<Product> selectAllProduct();
          Product selectProductById(String id);
          List<Product> selectProductWhenPriceLessThan(double price);
          List<Product> selectProductWhenPriceGreaterThan(double price);
          boolean insertProduct(Product product);
          boolean updateProduct(String oldId,Product newProduct);
          int deleteProductById(String id);
          int deleteAll();
      }
      
    • Mapper映射文件-ProductMapper.xml
      <mapper namespace="com.blacol.mapper.ProductMapper">
      <insert id="insertProduct">
          insert into Product values(default,#{name},#{price})
      </insert>
      <update id="updateProduct">
          update Product set name=#{arg1.name},price=#{arg1.price} where id=#{arg0}
      </update>
      <delete id="deleteProductById">
          delete from Product where id=#{id}
      </delete>
      <delete id="deleteAll">
          delete from Product
      </delete>
      
      <select id="selectAllProduct" resultType="Product">
          select * from Product
      </select>
      <select id="selectProductById" resultType="com.blacol.entity.Product">
          select * from Product where id=#{id}
      </select>
      <select id="selectProductWhenPriceLessThan" resultType="com.blacol.entity.Product">
          select * from Product where price &lt; #{price}
      </select>
      <select id="selectProductWhenPriceGreaterThan" resultType="com.blacol.entity.Product">
          select * from Product where price &gt; #{price}
      </select>
      </mapper>
      
  5. 编写Mybatis配置文件-mybatis-cfg.xml
    在src/main/resources文件夹下创建该文件

    <configuration>
    <typeAliases>
        <package name="com.blacol.entity"/>
    </typeAliases>
    <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:///mybatisLesson"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <package name="com.blacol.mapper"/>
    </mappers>
    </configuration>
    
  6. 编写测试类进行测试
    在test/java下创建一个测试类MybatisTest。测试类中有一成员session和一个无参构造方法。

    private SqlSession session=DBUtil.getSession();
    public MybatisTest() throws IOException {
    }
    
    • 测试添加
      测试类中添加方法insertTest()
      @Test//要导入JUnit包不要导入test包
      public void insertTest(){
          List<Product> products=new ArrayList<>();
          ProductMapper mapper = session.getMapper(ProductMapper.class);
          Product product=new Product();
          product.setName("面包");
          product.setPrice(3.5);
          products.add(product);
          product=new Product();
          product.setName("香肠");
          product.setPrice(2.2);
          products.add(product);
          product=new Product();
          product.setName("果酱");
          product.setPrice(5.2);
          products.add(product);
          for (Product p:products){
              if(mapper.insertProduct(p)){
                  session.commit();//默认的session是手动提交的
                  System.out.println("插入成功");
              }else{
                  session.rollback();
                  System.out.println("插入失败");
              }
          }
      }
      
      运行,此时报错,提示Invalidate Bound,这是因为maven在打包的时候不会把非resources文件夹下的mapper.xml文件打包。可以在pom.xml中添加下列代码解决:
          <build><!--build标签位于project标签内-->
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.xml</include><!--让maven打包java文件夹及子文件夹中的所有.xml文件-->
                    </includes>
                </resource>
            </resources>
        </build>
      

      再次运行我们就可以看到有3条插入成功在控制台中。


      step8-1 插入结果
    • 查找测试
      • 查找全部
        在测试类中添加方法selectAllAndIDTest
        @Test
        public void selectAllAndIDTest(){
            ProductMapper mapper = session.getMapper(ProductMapper.class);
            System.out.println("---获取所有商品---");
            List<Product> products = mapper.selectAllProduct();
            products.forEach(e-> System.out.println(e.getId()+"-"+e.getName()+"-"+e.getPrice()));
            System.out.println("---获取id为2的商品---");
            Product product = mapper.selectProductById("2");
            System.out.println(product.getId()+"-"+product.getName()+"-"+product.getPrice());
        }
        

      运行后可以看到结果:


      • 按照价格查找商品
        在测试类中添加方法selectByPriceTest
        @Test
        public void selectByPriceTest(){
            ProductMapper mapper = session.getMapper(ProductMapper.class);
            System.out.println("获取价格小于3元的商品");
            List<Product> products = mapper.selectProductWhenPriceLessThan(3);
            products.forEach(p-> System.out.println(p.getName()+"-"+p.getPrice()));
            System.out.println("获取价格大于3元的商品");
            List<Product> products2 = mapper.selectProductWhenPriceGreaterThan(3);
            products2.forEach(p-> System.out.println(p.getName()+"-"+p.getPrice()));
        }
        

        运行后可以看到相关的商品列出:


    • 更新商品
      在测试类中创建测试方法updateTest()
      @Test
      public void updateTest(){
          ProductMapper mapper = session.getMapper(ProductMapper.class);
          System.out.println("修改3号商品:");
          Product newProduct=new Product();
          newProduct.setName("冬瓜");
          newProduct.setPrice(1456.25);
          boolean b = mapper.updateProduct("3", newProduct);
          if(b){
              session.commit();
              System.out.println("更新成功");
          }else{
              session.rollback();
              System.out.println("更新失败");
          }
      }
      
      step8-3
    • 删除商品
      创建测试方法deleteTest()
      @Test
      public void deleteTest(){
          ProductMapper mapper = session.getMapper(ProductMapper.class);
          System.out.println("删除1号:");
          int i = mapper.deleteProductById("1");
          System.out.println("删除了"+i+"条数据");
          System.out.println("删除全部货物");
          i=mapper.deleteAll();
          System.out.println("删除了"+i+"条数据");
      }
      

    ![step8-4](https://img.haomeiwen.com/i6120602/f7c1e19db8dc15fc.png?imageMogr2/a@Mapper
    public interface UserMapper {
    List<User> selectAllUsers();
    List<User> selectUsersByIdards(List<IdCard> idCards);
    }uto-orient/strip%7CimageView2/2/w/1240)

相关文章

网友评论

      本文标题:Mybatis-2-1.SqlSession和Mapper

      本文链接:https://www.haomeiwen.com/subject/oxmhjrtx.html