MyBatis学习笔记——1

MyBatis学习笔记——1

  • 一、MyBatis概述
    • 1.1、框架
    • 1.2、三层架构
    • 1.4、了解MyBatis
    • 1.4、ORM思想
  • 二、MyBatis入门程序
    • 2.1、Mybatis程序的编写
    • 2.2、MyBatis入门程序的一些小细节
    • 2.3、MyBatis事务管理机制深度解析
    • 2.4、在开发中junit是如何使用的
    • 2.5、Mybatis集成日志框架logback
  • 三、使用MyBatis完成CRUD
    • 3.1、 insert(Create)
    • 3.2、delete(Delete)
    • 3.3、update(Update)
    • 3.4、select(Retrieve)
    • 3.5、关于SQL Mapper的namespace

一、MyBatis概述

1.1、框架

在文献中看到的framework被翻译为框架

  • Java常用框架:
    • SSM三大框架: Spring + SpringMVC + MyBatis
    • SpringBoot
    • SpringCloud
    • 等。。。
  • 框架其实就是对通用代码的苗装,提前写好了一堆接口和类,我们可以在做项目的时候直接引入这些接口和类(引入框架),基于这些现有的接口和类进行开发,可以大大提高开发效率.
  • 框架一般都以jar包的形式存在。(jar包中有class文件以及各种配置文件等。)
  • SSM三大框架的学习顺序:MyBatis、Spring、SpringMVC

1.2、三层架构

在这里插入图片描述

  • 表现层(UI)︰直接跟前端打交互(一是接收前端ajax请求,二是返回json数据给前端)
  • 业务逻辑层(BLL)︰一是处理表现层转发过来的前端请求(也就是具体业务),二是将从持久层获取的数据返回到表现层。
  • 数据访问层(DAL)︰直接操作数据库完成CRUD,并将获得的数据返回到上一层(也就是业务逻辑层)。
  • Java持久层框架
    • Mybatis
    • Hibernate(实现了JPA规范)
    • jOOQ
    • Guzz
    • Spring Data(实现了JPA规范)
    • ActiveJDBC

1.4、了解MyBatis

  • MyBatis本质上就是对JDBC的封装,通过MyBatis完成CRUD。
  • MyBatis在三层架构中负责持久层的,属于持久层框架。
  • MyBatis的发展历程:
    • MyBatis本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。.2013年11月迁移到Gitub,
    • iBATIS一词来源于“internet”和"abatis”的组合,是一个基于Java的持久层框架。iBATS提供的持久层框架包括SQL Maps和Data Access Obijects(DAOs),
  • 打开mybatis代码可以看到它的包结构中包含: ibatis

1.4、ORM思想

  • ORM:对象关系映射
    • O(Object):Java虚拟机中的Java对象。
    • R(Relational):关系型数据库
    • M(Mapping):将Java虚拟机中的Java对象映射到数据库表中一行记录,或是将数据库表中一行记录映射成Java虚拟机中的一个Java对象。
      ORM图示

在这里插入图片描述

  • MyBatis框架特点:
    • 支持定制化 SQL、存储过程、基本映射以及高级映射
    • 避免了几乎所有的 JDBC 代码中手动设置参数以及获取结果集
    • 支持XML开发,也支持注解式开发。【为了保证sql语句的灵活,所以mybatis大部分是采用XML方式开发。】
    • 将接口和 Java 的 POJOs(Plain Ordinary Java Object,简单普通的Java对象)映射成数据库中的记录
    • 体积小好学:两个jar包,两个XML配置文件。
    • 完全做到sql解耦合。
    • 提供了基本映射标签。
    • 提供了高级映射标签。
    • 提供了XML标签,支持动态SQL的编写。

二、MyBatis入门程序

只要你会JDBC,MyBatis就可以学。

2.1、Mybatis程序的编写

步骤1:打包方式:jar(不需要war,因为mybatis封装的是jdbc。)

<groupId>com.powernode</groupId>
<artifactId>mybatis-001-introduction</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

步骤2:引入依赖(mybatis依赖 + mysql驱动依赖)

<!--mybatis核心依赖-->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.5.10</version>
</dependency>
<!--mysql驱动依赖-->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.30</version>
</dependency>

步骤3:在resources根目录下新建mybatis-config.xml配置文件(可以参考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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/powernode"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--sql映射文件创建好之后,需要将该文件路径配置到这里-->
        <mapper resource=""/>
    </mappers>
</configuration>
  • 注意1:mybatis核心配置文件的文件名不一定是mybatis-config.xml,可以是其它名字。
  • 注意2:mybatis核心配置文件存放的位置也可以随意。这里选择放在resources根下,相当于放到了类的根路径下。

步骤4:在resources根目录下新建CarMapper.xml配置文件(可以参考mybatis手册拷贝)

<?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先随意写一个-->
<mapper namespace="car">
    <!--insert sql:保存一个汽车信息-->
    <insert id="insertCar">
        insert into t_car
            (id,car_num,brand,guide_price,produce_time,car_type) 
        values
            (null,'102','丰田mirai',40.30,'2014-10-05','氢能源')
    </insert>
</mapper>
  • 注意1:sql语句最后结尾可以不写“;”
  • 注意2:CarMapper.xml文件的名字不是固定的。可以使用其它名字。
  • 注意3:CarMapper.xml文件的位置也是随意的。这里选择放在resources根下,相当于放到了类的根路径下。
  • 注意4:将CarMapper.xml文件路径配置到mybatis-config.xml:
<mapper resource="CarMapper.xml"/>

步骤5:编写MyBatisIntroductionTest代码

package com.powernode.mybatis;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;
public class MyBatisIntroductionTest {
    public static void main(String[] args) {
        // 1. 创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        // 2. 创建SqlSessionFactory对象
        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
        // 3. 创建SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 4. 执行sql
        int count = sqlSession.insert("insertCar"); // 这个"insertCar"必须是sql的id
        System.out.println("插入几条数据:" + count);
        // 5. 提交(mybatis默认采用的事务管理器是JDBC,默认是不提交的,需要手动提交。)
        sqlSession.commit();
        // 6. 关闭资源(只关闭是不会提交的)
        sqlSession.close();
    }
}
  • 注意1:默认采用的事务管理器是:JDBC。JDBC事务默认是不提交的,需要手动提交。

2.2、MyBatis入门程序的一些小细节

  1. mybatis中sql语句的结尾;可以省略
  2. Resources.getResourceAsStream
    • 小技巧:以后凡是遇到esource这个单词,大部分情况下,这种加载资源的方式就是从类的根路径下开始加载。(开始查找)
    • 优点:采用这种方式,从类路径当中加载资源,项目的移植性很强。项目从windows移植到linux,代码不需要修改,因为这个资源文件一直都在类路径当中
  3. InputStream is = new FileInputStream(" d : \\mybatis-config.xml");
    • 采用这种方式也可以。
    • 缺点:可移植性太差,程序不够健壮。可能会移植到其他的操作系统当中。导致以上路径无效,还需要修改java代码中的路径。这样违背了OCP原则。
  4. 已经验证了:
    • mybatis核心配置文件的名字,不一定是: mybatis-config.xml。可以是其它名字。
    • mybatis核心配置文件存放的路径,也不一定是在类的根路径下。可以放到其它位置。但为了项目的移植性,健壮性,最好将这个配置文件放到类路径下面。
  5. InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
    • classLoader.getsystemclassLoader()获取系统的类加载器。
    • 系统类加载器有一个方法叫做: getResourceAsStream
    • 它就是从类路径当中加载资源的。
    • 通过源代码分析发现:
      InputStream is = Resources.getResourceAsStream( "mybatis-config.xml");底
      层的源代码其实就是:
      InputStream is = ClassLoader .getSystemClassLoader().getResourceAsStream("mybatis-config.xm1");
  • CarMapper.xml文件的名字是固定的吗? CarMapper.xml文件的路径是固定的吗?
    都不是固定的。
    <mapper resource="CarMapper.xml" /> resource属性:这种方式是从类路径当中加载资源。
    <mapper url="file:///d :/CarMapper.xml"/> url属性:这种方式是从绝对路径当中加载资源。

2.3、MyBatis事务管理机制深度解析

  1. 在mybatis-config.xml文件中,可以通过以下的配置进行mybatis的事务管理
    <transactionManager type="JDBC"/>

  2. type属性的值包括两个:

    • JDBC(jdbc)
    • MANAGED(managed)
      type后面的值,只有以上两个值可选,不区分大小写。
  3. 在mybatis中提供了两种事务管理机制:

    • 第一种:JDBC事务管理器
    • 第二种:MANAGED事务管理器
  4. JDBC事务管理器:
    mybatis框架自己管理事务,自己采用原生的JDBC代码去管理事务:
    conn.setAutocommit(false);开启事务。
    …业务处理…
    conn.commit();手动提交事务
    使用JDBC事务管理器的话,底层创建的事务管理器对象: JdbcTransaction对象。

    如果你编写的代码是下面的代码:
    sqlSession sqlsession = sqlsessionFactory.openSession(true);
    表示没有开启事务。因为这种方式压根不会执行:conn.setAutoCommit(false);
    在JDBC事务中,没有执行conn.setAutoCommit(false);那么autoCommit就是true
    如果autoCommit是true,就表示没有开启事务。只要执行任意一条DAL语句就提交一次。

  5. MANAGED事务管理器:
    mybatis不再负责事务的管理了。事务管理交给其它容器来负责。例如: spring。我不管事务了,你来负责吧。

    对于我们当前的单纯的只有mybatis的情况下,如果配置为:MANAGED那么事务这块是没人管的。没有人管理事务表示事务压根没有开启。

    没有人管理事务就是没有事务。

  6. JDBC中的事务:
    如果你没有在JDBC代码中执行: conn.setAutoCommit(false);的话,默认的autoCommittrue

2.4、在开发中junit是如何使用的

  • JUnit是专门做单元测试的组件。
    • 在实际开发中,单元测试一般是由我们Java程序员来完成的。
    • 我们要对我们自己写的每一个业务方法负责任,要保证每个业务方法在进行测试的时候都能通过。
    • 测试的过程中涉及到两个概念:
      • 期望值
      • 实际值
    • 期望值和实际值相同表示测试通过,期望值和实际值不同则单元测试执行时会报错。
  • 这里引入JUnit是为了代替main方法。
  • 使用JUnit步骤:
    • 第一步:引入依赖
    <!-- junit依赖 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
    
    • 第二步:编写单元测试类【测试用例】,测试用例中每一个测试方法上使用@Test注解进行标注。
      • 测试用例的名字以及每个测试方法的定义都是有规范的:
        • 测试用例的名字:XxxTest
        • 测试方法声明格式:public void test业务方法名(){}
    // 测试用例
    public class CarMapperTest{
        
        // 测试方法
        @Test
        public void testInsert(){}
        
        @Test
        public void testUpdate(){}
        
    }
    
    • 第三步:可以在类上执行,也可以在方法上执行
      • 在类上执行时,该类中所有的测试方法都会执行。
      • 在方法上执行时,只执行当前的测试方法。

2.5、Mybatis集成日志框架logback

关于mybatis集成日志组件,让我们调试起来更加方便

  • mybatis常见的集成的日志组件有哪些呢

    • SLF4J(沙拉风)
    • LOG4J
    • STDOUT_LOGGING
  • 其中STDOUT_LOGGING是标准日志,mybatis已经实现了这种标准日志。mybatis框架本身已经实现了这种标准只要开启即可。怎么开启呢?
    mybatis-config.xml文件中使用settings标签进行配置开启。

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

标准日志也可以用,但是配置不够灵活,可以集成其他的日志组件,例如:log4j,logback等。

  • logback是目前日志框架中性能较好的,较流行的,所以我们选它。
  • 引入logback的步骤:
    • 第一步:引入logback相关依赖
      <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.11</version>
        <scope>test</scope>
      </dependency>
      
      • 第二步:引入logback相关配置文件(文件名叫做logback.xmllogback-test.xml,放到类路径当中)
      <?xml version="1.0" encoding="UTF-8"?>
      
      <configuration debug="false">
          <!-- 控制台输出 -->
          <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
              <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                  <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
                  <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
              </encoder>
          </appender>
          <!-- 按照每天生成日志文件 -->
          <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
              <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                  <!--日志文件输出的文件名-->
                  <FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern>
                  <!--日志文件保留天数-->
                  <MaxHistory>30</MaxHistory>
              </rollingPolicy>
              <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                  <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
                  <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
              </encoder>
              <!--日志文件最大的大小-->
              <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
                  <MaxFileSize>100MB</MaxFileSize>
              </triggeringPolicy>
          </appender>
      
          <!--mybatis log configure-->
          <logger name="com.apache.ibatis" level="TRACE"/>
          <logger name="java.sql.Connection" level="DEBUG"/>
          <logger name="java.sql.Statement" level="DEBUG"/>
          <logger name="java.sql.PreparedStatement" level="DEBUG"/>
      
          <!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR -->
          <root level="DEBUG">
              <appender-ref ref="STDOUT"/>
              <appender-ref ref="FILE"/>
          </root>
      
      </configuration>
      

三、使用MyBatis完成CRUD

  • 准备工作
    • 创建module(Maven的普通Java模块):mybatis-002-crud
    • pom.xml
      • 打包方式jar
      • 依赖:
        • mybatis依赖
        • mysql驱动依赖
        • junit依赖
        • logback依赖
    • mybatis-config.xml放在类的根路径下
    • CarMapper.xml放在类的根路径下
    • logback.xml放在类的根路径下
    • 提供com.powernode.mybatis.utils.SqlSessionUtil工具类
    • 创建测试用例:com.powernode.mybatis.CarMapperTest

3.1、 insert(Create)

使用MAP集合传参
分析以下SQL映射文件中SQL语句存在的问题

<?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先随便写-->
<mapper namespace="car">
    <insert id="insertCar">
        insert into t_car(car_num,brand,guide_price,produce_time,car_type) values('103', '奔驰E300L', 50.3, '2022-01-01', '燃油车')
    </insert>
</mapper>

存在的问题是:SQL语句中的值不应该写死,值应该是用户提供的。之前的JDBC代码是这样写的:

// JDBC中使用 ? 作为占位符。那么MyBatis中会使用什么作为占位符呢?
String sql = "insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(?,?,?,?,?)";
// ......
// 给 ? 传值。那么MyBatis中应该怎么传值呢?
ps.setString(1,"103");
ps.setString(2,"奔驰E300L");
ps.setDouble(3,50.3);
ps.setString(4,"2022-01-01");
ps.setString(5,"燃油车");

在MyBatis中可以这样做:

  • 在Java程序中,将数据放到Map集合中
  • 在sql语句中使用 #{map集合的key} 来完成传值,#{} 等同于JDBC中的 ?#{}就是占位符

使用POJO传参

第一步:定义一个pojo类Car,提供相关属性。

package com.powernode.mybatis.pojo;

public class Car {
    private Long id;
    private String carNum;
    private String brand;
    private Double guidePrice;
    private String produceTime;
    private String carType;

    @Override
    public String toString() {
        return "Car{" +
                "id=" + id +
                ", carNum='" + carNum + '\'' +
                ", brand='" + brand + '\'' +
                ", guidePrice=" + guidePrice +
                ", produceTime='" + produceTime + '\'' +
                ", carType='" + carType + '\'' +
                '}';
    }

    public Car() {
    }

    public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) {
        this.id = id;
        this.carNum = carNum;
        this.brand = brand;
        this.guidePrice = guidePrice;
        this.produceTime = produceTime;
        this.carType = carType;
    }

    public Long getId() {
        return id;
    }

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

    public String getCarNum() {
        return carNum;
    }

    public void setCarNum(String carNum) {
        this.carNum = carNum;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Double getGuidePrice() {
        return guidePrice;
    }

    public void setGuidePrice(Double guidePrice) {
        this.guidePrice = guidePrice;
    }

    public String getProduceTime() {
        return produceTime;
    }

    public void setProduceTime(String produceTime) {
        this.produceTime = produceTime;
    }

    public String getCarType() {
        return carType;
    }

    public void setCarType(String carType) {
        this.carType = carType;
    }
}

第二步:Java程序

@Test
public void testInsertCarByPOJO(){
    // 创建POJO,封装数据
    Car car = new Car();
    car.setCarNum("103");
    car.setBrand("奔驰C200");
    car.setGuidePrice(33.23);
    car.setProduceTime("2020-10-11");
    car.setCarType("燃油车");
    // 获取SqlSession对象
    SqlSession sqlSession = SqlSessionUtil.openSession();
    // 执行SQL,传数据
    int count = sqlSession.insert("insertCarByPOJO", car);
    System.out.println("插入了几条记录" + count);
}

第三步:SQL语句

<insert id="insertCarByPOJO">
  <!--#{} 里写的是POJO的属性名-->
  insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
</insert>

经过测试得出结论:

  • 如果采用map集合传参,#{} 里写的是map集合的key,如果key不存在不会报错,数据库表中会插入NULL
  • 如果采用POJO传参,#{} 里写的是get方法的方法名去掉get之后将剩下的单词首字母变小写(例如:getAge对应的是#{age}getUserName对应的是#{userName}),如果这样的get方法不存在会报错。

注意:其实传参数的时候有一个属性parameterType,这个属性用来指定传参的数据类型,不过这个属性是可以省略的

3.2、delete(Delete)

注意:当占位符只有一个的时候,${} 里面的内容可以随便写。

SQL语句这样写:

<delete id="deleteByCarNum">
  delete from t_car where car_num = #{SuiBianXie}
</delete>

Java程序这样写:

@Test
public void testDeleteByCarNum(){
    // 获取SqlSession对象
    SqlSession sqlSession = SqlSessionUtil.openSession();
    // 执行SQL语句
    int count = sqlSession.delete("deleteByCarNum", "102");
    System.out.println("删除了几条记录:" + count);
}

3.3、update(Update)

需求:修改id=34的Car信息,car_num为102,brand为比亚迪汉,guide_price为30.23,produce_time为2018-09-10,car_type为电车
在这里插入图片描述
SQL语句如下:

<update id="updateCarByPOJO">
  update t_car set 
    car_num = #{carNum}, brand = #{brand}, 
    guide_price = #{guidePrice}, produce_time = #{produceTime}, 
    car_type = #{carType} 
  where id = #{id}
</update>

Java代码如下:

    @Test
    public void testUpdateCarByPOJO(){
        // 准备数据
        Car car = new Car();
        car.setId(34L);
        car.setCarNum("102");
        car.setBrand("比亚迪汉");
        car.setGuidePrice(30.23);
        car.setProduceTime("2018-09-10");
        car.setCarType("电车");
        // 获取SqlSession对象
        SqlSession sqlSession = SqlSessionUtil.openSession();
        // 执行SQL语句
        int count = sqlSession.update("updateCarByPOJO", car);
        System.out.println("更新了几条记录:" + count);
    }

在这里插入图片描述
当然了,如果使用map传数据也是可以的。

3.4、select(Retrieve)

select语句和其它语句不同的是:查询会有一个结果集。

需求:查询id为1的Car信息

SQL语句如下:

<select id="selectCarById">
  select * from t_car where id = #{id}
</select>

Java程序如下:

@Test
public void testSelectCarById(){
    // 获取SqlSession对象
    SqlSession sqlSession = SqlSessionUtil.openSession();
    // 执行SQL语句
    Object car = sqlSession.selectOne("selectCarById", 1);
    System.out.println(car);
}

运行结果如下:

### Error querying database.  Cause: org.apache.ibatis.executor.ExecutorException: 
    A query was run and no Result Maps were found for the Mapped Statement 'car.selectCarById'.  【翻译】:对于一个查询语句来说,没有找到查询的结果映射。
    It's likely that neither a Result Type nor a Result Map was specified.						 【翻译】:很可能既没有指定结果类型,也没有指定结果映射。

运行后之前的异常不再出现了,这说明添加了resultType属性之后,解决了之前的异常,可以看出resultType是不能省略的。
在这里插入图片描述

通过观察发现:只有id和brand是一致的,其他字段名和属性名对应不上,这是不是导致null的原因呢?我们尝试在sql语句中使用as关键字来给查询结果列名起别名试试:

<select id="selectCarById" resultType="com.powernode.mybatis.pojo.Car">
  select 
    id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType 
  from 
    t_car 
  where 
    id = #{id}
</select>

在这里插入图片描述

通过测试得知,如果当查询结果的字段名和java类的属性名对应不上的话,可以采用as关键字起别名,当然还有其它解决方案,我们后面再看。

查询多条数据
需求:查询所有的Car信息。
SQL语句如下:

<!--虽然结果是List集合,但是resultType属性需要指定的是List集合中元素的类型。-->
<select id="selectCarAll" resultType="com.powernode.mybatis.pojo.Car">
  <!--记得使用as起别名,让查询结果的字段名和java类的属性名对应上。-->
  select
    id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType
  from
    t_car
</select>

Java代码如下:

@Test
public void testSelectCarAll(){
    // 获取SqlSession对象
    SqlSession sqlSession = SqlSessionUtil.openSession();
    // 执行SQL语句
    List<Object> cars = sqlSession.selectList("selectCarAll");
    // 输出结果
    cars.forEach(car -> System.out.println(car));
}

3.5、关于SQL Mapper的namespace

在SQL Mapper配置文件中<mapper>标签的namespace属性可以翻译为命名空间,这个命名空间主要是为了防止sqlId冲突的。

创建CarMapper2.xml文件,代码如下:

<?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="car2">
    <select id="selectCarAll" resultType="com.powernode.mybatis.pojo.Car">
        select
            id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType
        from
            t_car
    </select>
</mapper>

不难看出,CarMapper.xml和CarMapper2.xml文件中都有 id=“selectCarAll”
将CarMapper2.xml配置到mybatis-config.xml文件中。

<mappers>
  <mapper resource="CarMapper.xml"/>
  <mapper resource="CarMapper2.xml"/>
</mappers>

编写Java代码如下:

@Test
public void testNamespace(){
    // 获取SqlSession对象
    SqlSession sqlSession = SqlSessionUtil.openSession();
    // 执行SQL语句
    List<Object> cars = sqlSession.selectList("selectCarAll");
    // 输出结果
    cars.forEach(car -> System.out.println(car));
}
org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: java.lang.IllegalArgumentException: 
  selectCarAll is ambiguous in Mapped Statements collection (try using the full name including the namespace, or rename one of the entries) 
  【翻译】selectCarAll在Mapped Statements集合中不明确(请尝试使用包含名称空间的全名,或重命名其中一个条目)
  【大致意思是】selectCarAll重名了,你要么在selectCarAll前添加一个名称空间,要有你改个其它名字。

Java代码修改如下:

@Test
public void testNamespace(){
    // 获取SqlSession对象
    SqlSession sqlSession = SqlSessionUtil.openSession();
    // 执行SQL语句
    //List<Object> cars = sqlSession.selectList("car.selectCarAll");
    List<Object> cars = sqlSession.selectList("car2.selectCarAll");
    // 输出结果
    cars.forEach(car -> System.out.println(car));
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/42092.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【UE4 塔防游戏系列】08-敌人到达终点对玩家造成伤害

目录 效果 步骤 一、敌人到终点时扣除玩家生命值 二、显示玩家生命值 效果 可以看到敌人进入终点后&#xff0c;左上角的玩家生命值会减少。 步骤 一、敌人到终点时扣除玩家生命值 新建一个Actor蓝图类&#xff0c;命名为“BP_EnemyEndPlace”&#xff0c;用来表示终点…

python实现拼多多商品详情接口API

最近工作需要用到拼多多的一些接口&#xff0c;官方竟然没有提供&#xff0c;python的sdk&#xff0c;于是就自己简单的写了一个商品SKU接口的api。 1、代码 #!/usr/bin/python3# -*- coding: utf-8 -*-# Time : 2020/3/29 0021 下午 19:40# Author : xiaozhi&#xff01;…

Flutter:EasyLoading(loading加载、消息提示)

前言 官方虽然提供了内置的加载指示器和提示信息&#xff0c;但是功能比较简陋&#xff0c;这里推荐&#xff1a;flutter_easyloading CircularProgressIndicator CircularProgressIndicator()加粗样式 ScaffoldMessenger.of(context).showSnackBar(const SnackBar(// 提示…

浅谈无人机遥感图像拼接与处理方法

遥感&#xff08;RS-Remote Sensing&#xff09;——不接触物体本身&#xff0c;用传感器收集目标物的电磁波信息&#xff0c;经处理、分析后&#xff0c;识别目标物&#xff0c;揭示其几何、物理性质和相互关系及其变化规律的现代科学技术。 换言之&#xff0c;即是“遥远的感…

第六章内存保护单元(Cortex-M7 Processor)

目录 第六章内存保护单元 6.1关于MPU 6.2MPU功能描述 6.3MPU编程器模型 第六章内存保护单元 介绍MPU (Memory Protection Unit)。它包含以下部分: 关于第6-2页的MPU。MPU功能描述见第6-3页。MPU程序员模型在第6-4页。 6.1关于MPU MPU是内存保护的可选组件。处理器支持标准…

从MVC跨越到DDD微服务架构是如何演进的

微服务架构演进 领域模型中对象的层次从内到外依次是&#xff1a;值对象、实体、聚合和限界上下文。 实体或值对象的简单变更&#xff0c;一般不会让领域模型和微服务发生大变。但聚合的重组或拆分却可以。因为聚合内业务功能内聚&#xff0c;能独立完成特定业务。那聚合的重组…

解决appium-doctor报ffmpeg cannot be found

一、下载ffmpeg安装包 https://ffmpeg.org/download.html 找到如图所示红框位置点击下载ffmpeg安装包。 二、配置ffmpeg环境变量 三、检查ffmpeg版本信息 重新管理员打开dos系统cmd命令提示符&#xff0c;输入ffmpeg查看是否出现版本信息&#xff0c;安装完好。 ffmpeg

D354周赛复盘:特殊元素平方和+数组最大美丽值(滑动窗口)+合法分割最小下标

文章目录 6889.特殊元素平方和思路完整版取模注意&#xff1a;不能对0取余/取模解答错误&#xff1a;本题的数组最后一个下标是nums[nums.size()] 6929.数组的最大美丽值&#xff08;排序滑动窗口&#xff09;思路1&#xff1a;排序滑动窗口注意点 6927. 合法分割的最小下标&am…

7.4Java EE——Bean的作用域

一、singleton作用域 Spring支持的5种作用域 作用域名城 描述 singleton 单例模式。在单例模式下&#xff0c;Spring 容器中只会存在一个共享的Bean实例&#xff0c; 所有对Bean的请求&#xff0c;只要请求的id&#xff08;或name&#xff09;与Bean的定义相匹配&#xff0…

css之flex两端对齐,且元素自动换行、flex、flow

文章目录 效果图htmlstyleflex-flow 效果图 html <div class"parent_element"><div class"item">7</div><div class"item">7</div><div class"item">7</div><div class"item"…

vue3 ts vite electron开发桌面程序

1、搭建vuetsvite项目 # 创建Vue项目 npm init vue # 安装依赖 npm install # 一定要安装成开发依赖 npm install electron electron-builder -D 根目录创建plugins文件夹&#xff0c;文件夹中创建ts文件&#xff0c;vite.electron.build.ts是打包文件代码&#xff0c;v…

spring复习:(22)实现了BeanNameAware等Aware接口的bean,相应的回调方法是在哪里被调用的?

AbstractAutowireCapableBeanFactory的doCreateBean用来创建bean, 其中调用了initializeBean方法对bean进行初始化 initializeBean包含如下代码&#xff1a; 而invokeAwareMethods代码如下&#xff1a; 可见其分别判断是否实现了BeanNameAware接口、BeanClassLoaderAware接口…

【java】【基础2】程序流程控制

目录 一、最经典的三种执行顺序 二、分支结构 2.1 if 2.2 switch 2.3 if与switch区别 三、循环结构 3.1 for循环 3.2 while循环 3.3 do-while循环 3.4 三种循环区别 3.5 补充知识&#xff1a;死循环 3.6 补充知识&#xff1a;循环嵌套 四、跳转关键字&#xff1a;br…

win7系统电脑怎么在桌面上悬挂工作日程安排清单显示呢?

在现代快节奏的工作环境中&#xff0c;合理安排和管理工作日程是非常重要的。而在电脑桌面上悬挂工作日程安排清单显示&#xff0c;可以让我们随时了解自己的任务和工作进度&#xff0c;提高工作效率。那么&#xff0c;如何在Win7系统电脑上实现这一功能呢&#xff1f; 今天我…

华为云ROMA Connect 的智能集成 – 现代企业数字化转型的新利器

在当今数字信息智能化的时代&#xff0c;人工智能技术逐渐深入企业的生产流程和实践中。人工智能的应用成为现代企业数字化转型的新利器。华为云的ROMA Connect作为企业级的融合集成服务 EiPaaS平台&#xff0c;从今年开始也进入了人工智能技术&#xff0c;针对几个主要的企业集…

selenium测试框架快速搭建(UI自动化测试)

一、介绍 selenium目前主流的web自动化测试框架&#xff1b;支持多种编程语言Java、pythan、go、js等&#xff1b;selenium 提供一系列的api 供我们使用&#xff0c;因此在web测试时我们要点页面中的某一个按钮&#xff0c;那么我们只需要获取页面&#xff0c;然后根据id或者na…

线程池学习(二)execute() 和 submit() 的区别

转载&#xff1a;线程池 线程提交的两种方式 ExecutorService poll3 Executors.newCachedThreadPool();for (int i 0; i < 2; i) {poll3.execute(new TargetTask());poll3.submit(new TargetTask());}execute方法 void execute(Runnable command): Executor接口中的方法s…

VSCode 注释后光标快速定位下一行

VSCode默认用 Ctrl / 注释一行时&#xff0c;光标停留在该行中。下面介绍如何注释后&#xff0c;光标会自动移动到下一行。 1.【View】 ->【Extensions】->【查找并安装Multi-command 扩展】 2.【File 】 -> 【Preferences 】->【Keyboard Shortcuts】&#xff08…

【数据结构】朴素模式匹配 KMP算法

&#x1f387;【数据结构】朴素模式匹配 & KMP 算法&#x1f387; &#x1f308; 自在飞花轻似梦,无边丝雨细如愁 &#x1f308; &#x1f31f; 正式开始学习数据结构啦~此专栏作为学习过程中的记录&#x1f31f; 文章目录 &#x1f387;【数据结构】朴素模式匹配 & K…

物理机传输大文件到虚拟机

物理机快速传输大文件到虚拟机 测试使用Tabby传输大文件到虚拟机 1.1 准备大文件 1.2 通过Tabby上传文件到Linux 总耗时约&#xff1a;7分钟 1.3 通过EveryThing配置服务 打开EveryThing&#xff0c;点击工具—> 选项—>http服务器 启用HTTP服务器&#xff0c;配置…