title: 1-MyBatis基本使用
toc: true
tags:
学习思路
使用步骤
基本使用例子
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”==);
// 使用指定数据库
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);
<?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>
注意
default 配置默认的 environment id
事务管理器
采用JDBC的原生事务机制:
- 开启事务:conn.setAutoCommit(false);
- 处理业务……
- 提交事务:conn.commit();
MANAGED 交给容器去管理事务 —>一般可以交给 spring 或者 springboot 去管理,当 mybatis 找不到容器支持时:也是没有事务。
不区分大小写
可以 使用 properties 外部文件
导入文件
<!--引入外部属性资源文件--> <properties resource="jdbc.properties"> <property name="jdbc.username" value="root"/> <property name="jdbc.password" value="root"/> </properties
- properties 有两个属性 resource 和 url
- resource 从类的根路径开始找
- url 则从指定的url加载,假设文件放在d:/jdbc.properties,这个url可以写成:file:///d:/jdbc.properties。注意是三个斜杠哦。
使用 变量
${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>
mappers 也有两个属性 与 properties 相同。
- resource 从类的根路径开始找
- url 则**从指定的url加载
但凡为 程序提供 Connection 对象的都叫做数据源
<dataSource type="POOLED">
type 设置类别三选一 type=”[UNPOOLED|POOLED|JNDI
initial_context
– 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。data_source
– 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。使用步骤
传参
类别
按数量划分
底层原理:
在多个参数的情况下,mybatis 会在底层创建一个 mapper 集合。
将传入的参数 封装为 arg0/param0 为key的集合。
#{xxx}
xxx应该写 arg0 按传入参数的下表开始。
select * from t_student where name = #{arg0} and sex = #{arg1}
是好处也是痛处 —->解决方案 @Param 注解 来写名字
案例分析
需求:通过name和sex查询。
/**
* 根据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>
执行结果:
异常信息描述了:name参数找不到,可用的参数包括[arg1, arg0, param1, param2]
修改StudentMapper.xml配置文件:尝试使用[arg1, arg0, param1, param2]去参数
<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>
运行结果:
再次尝试修改StudentMapper.xml文件
<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>
通过测试可以看到:
实现原理:实际上在mybatis底层会创建一个map集合,以arg0/param1为key,以方法上的参数为value,例如以下代码:
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}这种形式。
可以不用arg0 arg1 param1 param2吗?这个map集合的key我们自定义可以吗?当然可以。使用@Param注解即可。这样可以增强可读性。
实际上在mybatis底层会创建一个map集合,以arg0/param1为key,以方法上的参数为value.
可以通过 @Param 指定 传入参数 在 mybatis 底层创建 的mapper 对应的 keyName。
使用方法
@Param("keyName")
使用案例
List<Student> selectByNameAndAge(@Param(value="name") String name, @Param("age") int age);
注意
使用 @param 直接后,arg0 和 argxxx 会失效,但 param0 和 param1 还可以使用。
注解的原理。
直接传入参数即可,mybatis 会直接将传入的值添加到 对应的地方。
底层是 将占位符 #{xxx} 直接转换为 ?然后调用 selectOne 方法。即xxx里填什么都没关系。
前提是:主键是自动生成的。
业务背景:一个用户有多个角色。
插入一条新的记录之后,自动生成了主键,而这个主键需要在其他表中使用时。
插入一个用户数据的同时需要给该用户分配角色:需要将生成的用户的id插入到角色表的user_id字段上。
第一种方式:可以先插入用户数据,再写一条查询语句获取id,然后再插入user_id字段。【比较麻烦】
第二种方式:mybatis提供了一种方式更加便捷。
方法步骤
在 mapper.xml 映射文件中
1.配置 useGeneratedKeys=”true”
keyProperty=”储存主键的字段名。”
例如
<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 得到
@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());
}
使用这种方式的前提是:属性名遵循Java的命名规范,数据库表的列名遵循SQL的命名规范。
Java命名规范:首字母小写,后面每个单词首字母大写,遵循驼峰命名方式。
SQL命名规范:全部小写,单词之间采用下划线分割。
比如以下的对应关系:
实体类中的属性名 | 数据库表的列名 |
---|---|
carNum | car_num |
carType | car_type |
produceTime | produce_time |
两种拼接方法
1.双引号大法 ---> 单引号不行,已经试过了。
"%"#{} "#"
2.concat 拼接大法
concat('#',#{band},'%')
需求:查询奔驰系列的汽车。【只要品牌brand中含有奔驰两个字的都查询出来。】
/**
* 根据品牌进行模糊查询
* @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));
}
执行结果:
第一种:concat函数
<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>
执行结果:
第二种:双引号方式
<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>
简要描述:
通过 javassit 生成 dao 接口的代理类。然后 使用字符拼接的方式实现 dao 接口 要实现的方法体。
细节。
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:
<mapper namespace="mapper.salgradeMapper"> -->接口的全类名 <insert id="insert"→方法名 </insert> useGeneratedKeys="true" keyProperty="grade" > insert into salgrade(grade,losal,hisal) values (null,#{losal},#{hisal}) </insert> </mapper>
一个技巧
where 1=1 不会影响条件