1-MyBatis基本使用

学习思路


  1. 学会怎么用
  2. 为什么这样用

使用步骤

  1. 创建 “mybatis-config.xml” 配置文件
  2. 创建好 “mapper.xml映射文件”,并在 “mybatis-config.xml”配置文件中配置好 mapper.xml 的路径
  3. 使用 SqlSessionFactoryBuilder创建—->sqlSessionFactory—-> sqlSession
  4. 调用 sqlSession(“mapper.xml中的id,如果有 namespace 则使用 namespace.id。”,”需要传递的参数“) 进行 CRUD

基本使用例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
try {
// 1.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 2.创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
// 3.创建SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4.执行SQL


int count = sqlSession.insert("insertCar");



System.out.println("更新了几条记录:" + count);
// 5.提交
sqlSession.commit();


} catch (Exception e) {
// 回滚
if (sqlSession != null) {
sqlSession.rollback();
}
e.printStackTrace();
} finally {
// 6.关闭
if (sqlSession != null) {
sqlSession.close();
}
}
}
}

①、一些基本使用方法

1.切换环境

sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(“mybatis-config.xml”), ==”environmentID”==);

1
2
3
4
5
// 使用指定数据库
SqlSessionFactory sqlSessionFactory1 = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "dev");
SqlSession sqlSession1 = sqlSessionFactory1.openSession(true);
int count1 = sqlSession1.insert("insertCar", car);
System.out.println("插入了几条记录:" + count1);

②、各个环节详细解释

1.mybatis-config.xml 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?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="dev">-->
<!--默认使用生产环境-->
<environments default="production">
<!-- -->
<!--开发环境-->
<environment id="dev">
==========================================
<!--配置事务管理-->
<transactionManager type="JDBC"/>
===========================================
<!--配置dataSoure数据源-->
<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>
<!--生产环境-->
<environment id="production">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
==========================================
<!--配置 mapper.xml文件-->
<mappers>
<mapper resource="CarMapper.xml"/>
</mappers>
</configuration>

注意

  1. default 配置默认的 environment id

  2. 事务管理器

    1. 采用JDBC的原生事务机制:

      • 开启事务:conn.setAutoCommit(false);
      • 处理业务……
      • 提交事务:conn.commit();
    2. MANAGED 交给容器去管理事务 —>一般可以交给 spring 或者 springboot 去管理,当 mybatis 找不到容器支持时:也是没有事务。

    3. 不区分大小写

  3. 可以 使用 properties 外部文件

    1. 导入文件

      1
      2
      3
      4
      5
      <!--引入外部属性资源文件-->
      <properties resource="jdbc.properties">
      <property name="jdbc.username" value="root"/>
      <property name="jdbc.password" value="root"/>
      </properties
      1. properties 有两个属性 resource 和 url
        1. resource 从类的根路径开始找
        2. url 则从指定的url加载,假设文件放在d:/jdbc.properties,这个url可以写成:file:///d:/jdbc.properties。注意是三个斜杠哦。
    2. 使用 变量

      1
      2
      3
      4
      5
      6
      7
      8
      ${parametersName}
      <dataSource type="POOLED">
      <!--${key}使用-->
      <property name="driver" value="${jdbc.driver}"/>
      <property name="url" value="${jdbc.url}"/>
      <property name="username" value="${jdbc.username}"/>
      <property name="password" value="${jdbc.password}"/>
      </dataSource>
  4. mappers 也有两个属性 与 properties 相同。

    1. resource 从类的根路径开始找
    2. url 则**从指定的url加载

1.1重要 修改数据源

但凡为 程序提供 Connection 对象的都叫做数据源

1
2
<dataSource type="POOLED">

type 设置类别三选一 type=”[UNPOOLED|POOLED|JNDI

  • UNPOOLED
    • 不使用连接池。
  • POOLED
    • 使用 mybatis 自己实现的数据库连接池
  • JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。这种数据源配置只需要两个属性:
    • initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。
    • data_source – 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。

2.mapper.xml 文件配置 与使用 配置传入参数


使用步骤

  • 1.配置扫描路径
    • 1.可以在 mybatis-config.xml 或 直接在 springboot 的配置文件中进行配置
  • 2.编写 sql语句
  • 3.调用并传参

传参

类别

按数量划分


一、多参数
方案:一、直接传参

​ 底层原理:

在多个参数的情况下,mybatis 会在底层创建一个 mapper 集合。

将传入的参数 封装为 arg0/param0 为key的集合。

#{xxx}

xxx应该写 arg0 按传入参数的下表开始。

1
select * from t_student where name = #{arg0} and sex = #{arg1}

是好处也是痛处 —->解决方案 @Param 注解 来写名字

案例分析

需求:通过name和sex查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    /**
* 根据name和sex查询
* @param name
* @param sex
* @return
*/
List<Student> selectByNameAndSex(String name, Character sex);
@Test
public void testSelectByNameAndSex(){
List<Student> students = mapper.selectByNameAndSex("张三", '女');
students.forEach(student -> System.out.println(student));
}
<select id="selectByNameAndSex" resultType="student">
select * from t_student where name = #{name} and sex = #{sex}
</select>

执行结果:

img

异常信息描述了:name参数找不到,可用的参数包括[arg1, arg0, param1, param2]

修改StudentMapper.xml配置文件:尝试使用[arg1, arg0, param1, param2]去参数

1
2
3
4
<select id="selectByNameAndSex" resultType="student">
<!--select * from t_student where name = #{name} and sex = #{sex}-->
select * from t_student where name = #{arg0} and sex = #{arg1}
</select>

运行结果:

img

再次尝试修改StudentMapper.xml文件

1
2
3
4
5
6
<select id="selectByNameAndSex" resultType="student">
<!--select * from t_student where name = #{name} and sex = #{sex}-->
<!--select * from t_student where name = #{arg0} and sex = #{arg1}-->
<!--select * from t_student where name = #{param1} and sex = #{param2}-->
select * from t_student where name = #{arg0} and sex = #{param2}
</select>

通过测试可以看到:

  • arg0 是第一个参数
  • param1是第一个参数
  • arg1 是第二个参数
  • param2是第二个参数

实现原理:实际上在mybatis底层会创建一个map集合,以arg0/param1为key,以方法上的参数为value,例如以下代码:

1
2
3
4
5
6
7
8
Map<String,Object> map = new HashMap<>();
map.put("arg0", name);
map.put("arg1", sex);
map.put("param1", name);
map.put("param2", sex);

// 所以可以这样取值:#{arg0} #{arg1} #{param1} #{param2}
// 其本质就是#{map集合的key}

注意:使用mybatis****3.4.2之前的版本时:要用#{0}和#{1}这种形式。

@Param注解—–>解决方案一的痛点

可以不用arg0 arg1 param1 param2吗?这个map集合的key我们自定义可以吗?当然可以。使用@Param注解即可。这样可以增强可读性。


实际上在mybatis底层会创建一个map集合,以arg0/param1为key,以方法上的参数为value.


可以通过 @Param 指定 传入参数 在 mybatis 底层创建 的mapper 对应的 keyName。

​ 使用方法

1
@Param("keyName")

使用案例

1
List<Student> selectByNameAndAge(@Param(value="name") String name, @Param("age") int age);

注意

使用 @param 直接后,arg0 和 argxxx 会失效,但 param0 和 param1 还可以使用。

注解的原理。

image-20230206235943191

方案:二、使用mapper集合
方案:三、使用 pojo(domain) 类
二、单参数

直接传入参数即可,mybatis 会直接将传入的值添加到 对应的地方。

底层是 将占位符 #{xxx} 直接转换为 ?然后调用 selectOne 方法。即xxx里填什么都没关系。

③、一些技巧

1.使用 sql 自动生成的主键,并将其保存到一个地方。

前提是:主键是自动生成的。

业务背景:一个用户有多个角色。

E9F189EB-F5E2-465f-828C-127DB34968FE.png

插入一条新的记录之后,自动生成了主键,而这个主键需要在其他表中使用时。

插入一个用户数据的同时需要给该用户分配角色:需要将生成的用户的id插入到角色表的user_id字段上。


第一种方式:可以先插入用户数据,再写一条查询语句获取id,然后再插入user_id字段。【比较麻烦】

第二种方式:mybatis提供了一种方式更加便捷。

方法步骤

  • 在 mapper.xml 映射文件中

    • 1.配置 useGeneratedKeys=”true”
      keyProperty=”储存主键的字段名。”
      1
      2
      3
      4
      5
      6
      7
      例如
      <insert id="insertUseGeneratedKeys" useGeneratedKeys="true" keyProperty="id">
      insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
      </insert>

      即可在传入的 对象中使用 对象.id 得到

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      @Test
      public void testInsertUseGeneratedKeys(){
      CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
      Car car = new Car();
      car.setCarNum("5262");
      car.setBrand("BYD汉");
      car.setGuidePrice(30.3);
      car.setProduceTime("2020-10-11");
      car.setCarType("新能源");
      mapper.insertUseGeneratedKeys(car);
      SqlSessionUtil.openSession().commit();
      ====================
      System.out.println(car.getId());
      }

2.结果映射


1).使用 resultmapper

② 使用驼峰命名自动映射。、

使用这种方式的前提是:属性名遵循Java的命名规范,数据库表的列名遵循SQL的命名规范。

Java命名规范:首字母小写,后面每个单词首字母大写,遵循驼峰命名方式。

SQL命名规范:全部小写,单词之间采用下划线分割。

比如以下的对应关系:

实体类中的属性名 数据库表的列名
carNum car_num
carType car_type
produceTime produce_time

3.创建 大 maper

4.模糊查询


两种拼接方法

1
2
3
4
1.双引号大法  ---> 单引号不行,已经试过了。
"%"#{} "#"
2.concat 拼接大法
concat('#',#{band},'%')

需求:查询奔驰系列的汽车。【只要品牌brand中含有奔驰两个字的都查询出来。】

使用${}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 根据品牌进行模糊查询
* @param likeBrank
* @return
*/
List<Car> selectLikeByBrand(String likeBrank);
<select id="selectLikeByBrand" resultType="Car">
select
id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from
t_car
where
brand like '%${brand}%'
</select>
@Test
public void testSelectLikeByBrand(){
CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
List<Car> cars = mapper.selectLikeByBrand("奔驰");
cars.forEach(car -> System.out.println(car));
}

执行结果:

img

使用#{}

第一种:concat函数

1
2
3
4
5
6
7
8
<select id="selectLikeByBrand" resultType="Car">
select
id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from
t_car
where
brand like concat('%',#{brand},'%')
</select>

执行结果:

img

第二种:双引号方式

1
2
3
4
5
6
7
8
<select id="selectLikeByBrand" resultType="Car">
select
id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from
t_car
where
brand like "%"#{brand}"%"
</select>

img

④、底层原理

一、getMapper 的原理


简要描述:

​ 通过 javassit 生成 dao 接口的代理类。然后 使用字符拼接的方式实现 dao 接口 要实现的方法体。


细节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
mybatis基本程序
// 1.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 2.创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
// 3.创建SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4.执行SQL
int count = sqlSession.insert("insertCar");

抽离核心
SqlSession sqlSession = sqlSessionFactory.openSession();
int count = sqlSession.insert("insertCar");
这两部每一个都不一样,其他1 2部相同。

// 获取sqlId(这里非常重要:因为这行代码导致以后namespace必须是接口的全限定接口名,sqlId必须是接口中方法的方法名。)

String sqlId = daoInterface.getName() + "." + methodName;
// 获取SqlCommondType
String sqlCommondTypeName = sqlSession.getConfiguration().getMappedStatement(sqlId).getSqlCommandType().name();
if ("SELECT".equals(sqlCommondTypeName)) {
methodStr.append("org.apache.ibatis.session.SqlSession sqlSession = com.powernode.bank.utils.SqlSessionUtil.openSession();");
methodStr.append("Object obj = sqlSession.selectOne(\"" + sqlId + "\", arg0);");
methodStr.append("return (" + returnTypeName + ")obj;");
} else if ("UPDATE".equals(sqlCommondTypeName)) {
methodStr.append("org.apache.ibatis.session.SqlSession sqlSession = com.powernode.bank.utils.SqlSessionUtil.openSession();");
methodStr.append("int count = sqlSession.update(\"" + sqlId + "\", arg0);");
methodStr.append("return count;");
}

String sqlId = daoInterface.getName() + “.” + methodName;

  • daointerface.getName() 获取了接口的全类名
  • methodName 前面获取了 daointerface 中对应的方法名

==这一步 限定了 mapper 文件里 的namespacce必须为接口的全类名,id为方法名。==

eg:

1
2
3
4
5
6
7
8
9
10
<mapper namespace="mapper.salgradeMapper"> -->接口的全类名
<insert id="insert"方法名 </insert> useGeneratedKeys="true" keyProperty="grade" >
insert into
salgrade(grade,losal,hisal)
values
(null,#{losal},#{hisal})

</insert>

</mapper>

⑤动态 SQL

1.拼接 where


一个技巧

where 1=1 不会影响条件