Mybatis笔记
mybatis的概述
简介
- MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
- Mybtais是一个ORM框架,轻量级的ORM框架。相对于重量级的ORM框架Hibernate而言,mybatis是一个半自动框架,而Hibernate是一个全自动框架。并且,Hibernate提出跨”平台”,Hinernate的跨平台是指Hinernate可以在多种数据库下进行操作,同一套代码支持多种数据库,HQL语句,Hibernate方言,对数据库操作进行翻译,根据不同的数据,将API翻译成不同的SQL,对数据进行操作,依赖ORM思想。
- Mybatis是一个半自动的框架,早期Hibernate在流行时,开发者发现Hibernate虽然功能强大,但是由于如果想使用全自动功能,将Hibernate和数据库关心进行配置,配置很繁琐,其二,Hibernate对功能进行全方面的封装,将用户的操作,转化为SQL语句,然后进行数据库操作,整个转换过程是Hibernate,开发无法控制,如果要进行SQL语句优化是没法实现的。所以,在数据库压力逐渐增大的情况下,Hibernate框架性能问题就出现了。基于这样的情况,Mybatis框架应运而生,mybatis将SQL语句的定义控制权,完全交给了开发者,并且暴露一套API,对JDBC中:事务,参数,查询结果等进行配置处理。Mybatis也是基于ORM思想.
ORM
ORM:Object relation mapping
对象关系映射
将数据库信息和Java中的实体类进行映射
mybatis入门
创建maven的Java项目
如图:
引入mybatis相关的jar包依赖
-
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--mybatis关联spring依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!--数据库驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!--连接池依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!--分页插件依赖-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
创建实体类user
public class User {
private Long id;
private String name;
private Integer age;
private int sex;
private String address;
private Date birthday;
}
编写映射文件usermapper接口
public interface UserMapper {
Map<String,Object> queryById1(Long id);
User queryById2(Long id);
}
编写映射文件usermapper.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="com.bjpowernode.mapper.UserMapper">
<!--mapper 是根标签 只有一对
namespace 是 这个标签的属性,必须要给
给的值为:mapper接口的全限定名 (必须)
-->
<!--select 查询标签 ,( 同类标签 update , delete, insert )
resultType 返回值类型,(类的全限定名) , 告知给Mybatis
把sql语句的返回值封装成什么类型的对象
标签体中 存放的是 sql
#{变量} 占位符(图像对象导航语言) 相当于 ?
select * from USER where id = #{id} 等价于
select * from USER where id = ?
id 表示唯一性 范围 :在当前xml文件中必须要唯一
-->
<select id="queryById1" resultType="java.util.Map">
select * from USER where id = #{id}
</select>
<select id="queryById2" resultType="com.bjpowernode.domain.User">
select * from USER where id = #{id}
</select>
编写mybatis-config.xml核心配置文件
<?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>
<!-- 环境配置 default 属性 必须和其中一个子标签中的 id 属性 一致 表示
当前使用的是id 对应的环境
开发周期中 ,环境有 开发环境, 测试环境,生产环境
id 属性 表示唯一性,在当前xml文件中必须要唯一
-->
<environments default="dev">
<environment id="dev">
<!--transactionManager 事务管理器 JDBC 是 MyBatis框架的默认事务管理器-->
<transactionManager type="JDBC"/>
<!--数据源(连接池) 目前使用的 是MyBatis默认的连接池 POOLed-->
<dataSource type="POOLED">
<!--连接数据库的 四要素-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///demo"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 关联 mapper的 映射文件 -->
<mappers>
<mapper resource="com/bjpowernode/mapper/UserMapper.xml"/>
</mappers>
</configuration>
编写测试类
public class MyBatisTest {
@Test
public void testFind() throws IOException {
//1. 加载 MyBatis 的 核心配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//2.加载inputStream 流 ,获取SqlSessionFactory 工厂对象
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3.获取 sqlSession对象
SqlSession sqlSession = sessionFactory.openSession();
// 4. 调用MyBatis接口层的API
/* 第一个参数 namespece 的值 +"." + sql的 id属性的值
* 第二个参数 sql语句中需要传入的参数*/
// User user = sqlSession.selectOne("com.bjpowernode.mapper.UserMapper.queryById2", 1L);
// Map
结果映射
#和$语法的区别
在mybatis中,会将开发者定义的sql进行解析,解析分为了2类sql:
静态sql ,在解析时,直接将参数拼接到sql中,这种就是静态sql
动态sql,在解析时,会使用?这个占位符,替代参数
这两种解析方式,mybatis是根据${}和#{}进行区分的
- ${}的sql是静态sql
- #{}的sql是动态sql
不论是静态sql,还是动态sql都能获取传递参数,但是${}是使用的字符拼接,#{}使用PreparedStatement进行参数的预处理。
在一定程度上说,${}能实现的功能,#{}都能实现,并且由于#{}PreparedStatement进行SQL的预处理,一定程度上可以防止SQL注入攻击。所以在开发中,能使用#{}尽量使用#{}。PreparedStatement预处理的本质是将参数进行转换为字符串,当做参数字符串处理。所以,如果参数信息是一个特殊的关键字,例如: 数据库名,表名,函数名,内置关键字,使用预处理,则关键字转为了字符串,无效了,此时必须使用字符串拼接。
sql片段
- 在开发中,需要书写大量的SQL语句,并且这些SQL数据很大部分内容是重复内容,基于这样的情况,mybatis提供模板,可以在模板中定义需要使用sql语句的部分内容,然后在需要使用到这个部分内容的地方直接引入。
- 使用sql标签,定义SQL片段,使用include引入,sql片段
模糊查询
- like concat(‘%’,关键字,’%’)
- like ‘%关键字%’
- mybatis推荐的bind标签
- 注意:建议只使用第一种方式
多参数问题
当mybatis传递参数存在多个时,mybatis支持三种方案:
argx 形式:arg 表示参数,x表示参数索引
paramX形式: param表示参数,x表示第几个参数
使用注解为参数取别名 @Param
分页查询
MySql 中 是通过limit 关键字 进行分页 的Limit ? , ?
第一个参数:表示 开始索引 (从0开始)
第二个参数:表示每页显示的条数
- 开始索引的值 = (当前页-1)*每页显示的条数
在mybatis中,分页查询解决方案最本质上回归原生SQL进行处理。
查询总页数,使用limit函数进行查询相应的数据
mybatis中,提供了一个RowBounds这个类,用于进行分页查询数据
使用分页插件:PageHelper(推荐)
新增自增长
在mybatis中,支持数据库的自增长功能,因为在某些特殊的业务场景中,当前数据的ID,可能是另外某些数据的业务ID。
例如:
- 订单:总订单和子订单,每个子订单会有总订单ID,先插入总订单,并且要获取总订单ID
插入子订单
Mybatis在insert指令中,提供了2个属性:useGeneratedKeys、keyProperty、keyColumn
useGeneratedKeys:表示使用数据库自增长
keyProperty:自增长的列对应的类中的属性
keyColumn:自增长的列
Mybatis会自动将增长的值,封装到传入参数的属性中。
<insert id="insert" parameterType="com.example.domain.User" useGeneratedKeys="true" keyProperty="id" keyColumn="id"> insert into user (name,pwd) value (#{name},#{pwd}) </insert>
public static void main(String[] args) throws Exception { SqlSession session = SqlSessionUtil.getSession(); //获取UserMapper接口的具体代理对象 UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(); user.setName("Lucy"); user.setPwd("123456"); System.out.println("新增后user对象:" + user.getId()); session.commit(); session.close(); }
动态sql
动态SQL是指根据不同的参数,产生不同的SQL语句。这样的SQL就是动态SQL。
Mybatis提供了一下标签,用于动态SQL的生成:
if
foreach
choose
where
set
if标签
- 在if标签中,test属性是必须有,test属性值是一个表达式,如果表达式值为true,则if标签包裹的内容会拼接在当前sql上。
- and 并且
- or 或者
- == 等于
- != 不等于
<select id="selectByCondition" resultMap="baseMap"> select <include refid="columnId"/> from user where 1=1 <if test="keyword!=null and keyword!=''"> and name like concat('%',#{keyword},'%') </if> </select>
foreach
Select * from user where id in (1,2,3,4)
循环标签,循环标签多用于批量操作。
例如:批量新增,批量删除,批量查询等等
collection 待循环的容器
item 指代 每次循环容器中的元素
open 开始循环是拼接字符串
close 循环结束拼接字符串
separator 每次循环之间拼接的字符串
index 循环索引
<!-- ( #{id},#{id},#{id},#{id} )--> <select id="selectByIds" resultMap="baseMap"> Select * from user where id in <foreach collection="ids" item="id" open="(" close=")" separator=","> #{id} </foreach> </select> <!-- <insert id="batchInsertUser">--> <!-- insert into user (name ,age,sex,birthday,address) values (#{name} ,#{age},#{sex},#{birthday},#{address}),(#{name} ,#{age},#{sex},#{birthday},#{address})--> <!-- </insert>--> <insert id="batchInsertUser"> insert into user (name ,age,sex,birthday,address) values <foreach collection="users" separator="," item="user"> (#{user.userName} ,#{user.age},#{user.sex},#{user.birthday},#{user.address}) </foreach> </insert> <delete id="deleteByIds"> delete from user where id in <foreach collection="array" item="id" close=")" open="(" separator=","> #{id} </foreach> </delete>
List<User> selectByIds(@Param("ids") List<Long> ids); void batchInsertUser(@Param("users") List<User> users); void deleteByIds(Long[] ids); @Test public void testDeleteByIds() { // 1.获取sqlSession对象 SqlSession sqlSession = MyBatisUtil.openSession(); // 2. 获取UserMapper的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); Long[] ids = {1L,2L,3L}; userMapper.deleteByIds(ids); } @Test public void testBatchInsertUsers(){ // 1.获取sqlSession对象 SqlSession sqlSession = MyBatisUtil.openSession(); // 2. 获取UserMapper的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> users = new ArrayList<User>(); User user = new User(); user.setUserName("lucy"); user.setSex(0); user.setAddress("武侯区"); user.setAge(10); user.setBirthday(new Date()); users.add(user); User user1 = new User(); user1.setUserName("结衣"); user1.setSex(0); user1.setAddress("双流区"); user1.setAge(20); user1.setBirthday(new Date()); users.add(user1); userMapper.batchInsertUser(users); } @Test public void testSelectByIds(){ // 1.获取sqlSession对象 SqlSession sqlSession = MyBatisUtil.openSession(); // 2. 获取UserMapper的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<Long> ids = new ArrayList<Long>(); ids.add(1L); ids.add(2L); ids.add(3L); List<User> list = userMapper.selectByIds(ids); for (User user : list) { System.out.println(user); } }
choose标签
多条件分支标签:choose.
在choose标签,自上而下执行when中表达式,如果表达式为true,则将相应的字符串拼接在sql后面,且终止判断。如果所有的表达式都为false,则将otherwise中字符串,拼接在sql后面
where标签
在mybatis中,存在sql条件,当有多个sql条件时,需要处理and关键字问题,因为where后面的第一个条件不需要and,解决方案:
在where后面 拼接 1=1 类似的条件,这样其他条件都不是第一个条件,都需要拼接and
mybatis 提供了where标签,取代where关键字,默认去掉第一个条件 and
注意:
- 建议,只在查询语句中使用where标签,因为当where标签中的条件都不成立时,会没有where关键字。
<select id="selectList2" parameterType="map" resultMap="BaseResultMap" > select <include refid="BASE_COLUMN" /> from user <where> <if test="name != null and name !=''"> and name like concat('%',#{name},'%') </if> <if test="sex != null and sex !=''"> and sex = #{sex} </if> </where> </select> <!-- 反面例子 如果name 值是空字符串 会怎么样?? --> <delete id="deleteByName" > delete from user <where> <if test="name != null and name !=''"> name = #{name} </if> </where> </delete>
set标签
- set标签是取代sql语句中的set关键字。set表示后面数据的更新。各个字段,需要使用逗号分隔。
- set标签可以去掉最后一个逗号。
<!-- where 标签 可以帮我们去掉 多余的and 连接符 set 标签 可以 帮我们去掉 多余的逗号 --> <update id="updateByCondition"> update user <set> <if test="userName!=null"> name=#{userName}, </if> <if test="sex!=null"> sex=#{sex}, </if> <if test="age!=null"> age=#{age}, </if> <if test="birthday!=null"> birthday=#{birthday}, </if> <if test="address!=null"> address=#{address}, </if> </set> where id = #{id} </update>
联表查询
关联查询方法
- 当属性中有需要赋值对象的属性时,该属性使用association关键字
// 需求:查询员工列表,以及对应的部门信息 (方案一: 关联查询)
@Test
public void testEmployeeList(){
SqlSession sqlSession = MyBatisUtil.openSession();
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
List<Employee> list = employeeMapper.list();
for (Employee employee : list) {
System.out.println("employee = " + employee);
}
}
<resultMap id="baseMap" type="com.bjpowernode.domain.Employee">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="email" property="email"/>
<result column="admin" property="admin"/>
<result column="age" property="age"/>
<result property="dept.id" column="d_id"/>
<result property="dept.name" column="d_name"/>
<!--association 用来封装 对象属性 (单个对象)
property 属性 (对应的是类中对象属性的名称)
子标签 (id ,result)
javaType (property属性的值)变量的类型, MyBatis 的底层 需要根据
javaType的值,通过反射创建对象,赋值给dept 变量
-->
<!-- <association property="dept" javaType="com.bjpowernode.domain.Department">-->
<!-- <!–id 标签 用在主键上面–>-->
<!-- <id column="d_id" property="id"/>-->
<!-- <!–result 标签 用在非主键上面–>-->
<!-- <result column="d_name" property="name"/>-->
<!-- </association>-->
<!--
columnPrefix 是对column 列的 前缀 进行抽取
-->
<association property="courses" javaType="com.bjpowernode.edu.domain.Course">
<id property="id" column="id"/>
<result property="courseName" column="course_name"/>
</association>
</resultMap>
<select id="list" resultMap="baseMap">
select emp.id,emp.username,emp.email,emp.age,emp.admin,d.id d_id,d.name d_name from employee emp
left join department d on emp.dept_id = d.id
</select>
额外sql方法
当属性中有需要赋值的是集合的属性时,使用collection 关键字
<resultMap id="baseMap" type="com.bjpowernode.domain.Employee"> <id column="id" property="id"/> <result column="username" property="username"/> <result column="password" property="password"/> <result column="email" property="email"/> <result column="admin" property="admin"/> <result column="age" property="age"/> <result property="dept.id" column="d_id"/> <result property="dept.name" column="d_name"/> <!-- collection 表示集合标签, 用来封装集合属性的(如果类中有集合(对象)属性,那么就选择是Collection) property 属性 表示 类中的集合属性的名称 columnPrefix 属性 表示 列的前缀抽取 ofType 属性 表示集合中的对象的类型 (可以省略,当是发送额外sql 的时候可以省略) --> <collection property="lessonList" ofType="courseLesson" column="id" select="com.bjpowernode.edu.mapper.CourseLessonMapper.selectCourseLessonById"/> </resultMap> <select id="list" resultMap="baseMap"> select id, username, email from employee </select>