Mybatis-2-1.SqlSession和Mapper

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://upload-images.jianshu.io/upload_images/6120602-f7c1e19db8dc15fc.png?imageMogr2/a@Mapper
    public interface UserMapper {
    List<User> selectAllUsers();
    List<User> selectUsersByIdards(List<IdCard> idCards);
    }uto-orient/strip%7CimageView2/2/w/1240)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容