Mybatis
IDEA配置环境
https://blog.csdn.net/qq_16804847/article/details/120409429?spm=1001.2014.3001.5502
pom.xml
<!-- 热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!--引入插件lombok 自动的set/get/构造方法插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--mybatis依赖包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!--jdbc依赖包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
热部署
配置自动编译
让热部署有效,快捷键: ctrl + alt + shift + /
勾选自动重启
插件Lombok
package com.jt.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.stereotype.Component;
import java.io.Serializable;
/**
* 实体对象要求:
* 1.类名一般与表名关联
* 2.属性名称一般与字段关联
* 3.pojo中的属性类型必须为引用类型(包装类型)
* 4.实体对象必须有get/set方法
* 5.一般实体对象需要实现序列化接口(规则)
* 原因: 数据可能跨平台(跨服务器)传输,必须序列化
*/
@Data //动态生成get/set/toString/equals等方法
@Accessors(chain = true) //开启链式加载 重写set方法
@NoArgsConstructor //无参构造
@AllArgsConstructor //有参构造
public class DemoUser implements Serializable {
private Integer id;
private String name;
private Integer age;
private String sex;
//this 运行期有效 代表当前对象
/*public DemoUser setId(Integer id){
this.id = id;
return this;
}*/
//方法测试
public void add(){
DemoUser user = new DemoUser();
user.setId(100)
.setName("aaaa")
.setAge(18)
.setSex("女");
}
}
Mybatis案例
编辑mybatis-config.xml
核心配置文件的顺序
The content of element type "configuration" must match "
(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".
自动驼峰映射,resultMap中不需要再写 < result column=“dept_name” property=“deptName” />
<?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>
<!-- 核心配置 -->
<settings>
<!-- 自动驼峰映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 配置别名 -->
<typeAliases>
<!-- <typeAlias type="com.jt.pojo.DemoUser" alias="DemoUser"/>-->
<package name="com.jt.pojo"/>
</typeAliases>
<!--环境配置标签-->
<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://127.0.0.1:3306/jt?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--Mybatis加载Mapper映射文件-->
<mappers>
<mapper resource="mybatis/mappers/DemoUserMapper.xml"/>
<mapper resource="mybatis/mappers/DeptMapper.xml"/>
<mapper resource="mybatis/mappers/EmpMapper.xml"/>
</mappers>
</configuration>
构建持久层接口
/**
* 说明:
* 1.根据面向接口开发的思想需要定义一个Mapper接口
* 2.在接口中可以写接口方法, 谁用谁去实现!!!
*/
public interface DemoUserMapper {
//查询所有的表数据
public List<DemoUser> findAll();
}
构建mapper接口的实现类的xml配置文件
DemoUserMapper.xml,sql简化,常规CURD操作
<?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是mybatis映射文件的唯一标识,与接口对应-->
<mapper namespace="com.jt.mapper.DemoUserMapper">
<!-- 简化sql标签 -->
<sql id="demo_dept_sql">
select id,name,age,sex from demo_user
</sql>
<!--id 表示接口方法
resultType 返回值结果类型
-->
<select id="findAll" resultType="DemoUser">
<include refid="demo_dept_sql" />
</select>
<!-- parameterType属性可以不写 -->
<insert id="saveDemoUser" parameterType="com.jt.pojo.DemoUser">
insert into demo_user(id,name,age,sex) values (null,#{name},#{age},#{sex})
</insert>
<update id="updateDemoUser" parameterType="com.jt.pojo.DemoUser">
update demo_user set name=#{name},age=#{age},sex=#{sex} where id=#{id}
</update>
<delete id="deleteDemoUser" parameterType="Integer">
delete from demo_user where id=#{id}
</delete>
</mapper>
DeptMapper.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">
<!--namespace是mybatis映射文件的唯一标识,与接口对应-->
<mapper namespace="com.jt.mapper.DeptMapper">
<!--
关于一对多 数据封装说明:
1.collection: 封装集合的固定写法.
property: 指定属性
ofType: 封装List集合的泛型对象
2.如果开启驼峰映射规则,可以简化赋值过程
3.autoMapping="true" 自动实现映射
4.一般最好保留主键,为后续扩展提供方便.
-->
<select id="findAll" resultMap="deptRM">
select id,name,age,dept.dept_id,dept_name from dept
left join emp on emp.dept_id = dept.dept_id
</select>
<!-- 自定义映射关系 -->
<resultMap id="deptRM" type="Dept" autoMapping="true">
<!-- 开启驼峰命名规则-->
<id column="dept_id" property="deptId"/>
<!-- <result column="dept_name" property="deptName"/>-->
<collection property="empList" ofType="Emp" autoMapping="true">
<id column="id" property="id"/>
</collection>
</resultMap>
</mapper>
EmpMapper.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">
<!--namespace是mybatis映射文件的唯一标识,与接口对应-->
<mapper namespace="com.jt.mapper.EmpMapper">
<!--
规则:
1.如果映射的字段与对象的属性一致,则可以省略不写.
2.最好保留主键的字段信息.
3.如果需要封装单个对象则使用association标签
3.1 property 代表封装对象的属性
3.2 javaType 指定属性的类型 注意路径
4.如果遇到关联封装,必须全部配置映射关系. 如果属性与字段名称一致
则可以使用autoMapping="true"实现自动映射
-->
<!-- 直接用where或left join来进行多表联查 -->
<select id="findAll" resultMap="empRM">
select id,name,age,dept.dept_id,dept_name from emp,dept
where emp.dept_id = dept.dept_id
</select>
<!-- 自定义映射关系 -->
<resultMap id="empRM" type="Emp" autoMapping="true">
<id column="id" property="id"/>
<!-- autoMapping="true"属性可以自动映射字段名和属性名 -->
<!-- <result column="name" property="name"/>-->
<!-- <result column="age" property="age"/>-->
<association property="dept" javaType="Dept"><!-- association可以注入实体对象 -->
<id column="dept_id" property="deptId"/>
<result column="dept_name" property="deptName"/>
</association>
</resultMap>
<!-- 将两个单表查询的select连接起来查询,where dept_id = #{deptId} -->
<select id="findAllWhere" resultMap="empRM2">
select id,name,age,dept_id from emp
</select>
<resultMap id="empRM2" type="Emp" autoMapping="true">
<id column="id" property="id"/>
<association property="dept" javaType="Dept" column="dept_id" select="findDept"/>
</resultMap>
<select id="findDept" resultMap="deptRM">
select dept_id,dept_name from dept where dept_id = #{deptId}
</select>
<resultMap id="deptRM" type="Dept">
<id column="dept_id" property="deptId"/>
<result column="dept_name" property="deptName"/>
</resultMap>
</mapper>
Mybatis实现数据查询
@Test
public void demo() throws IOException {
//指定配置文件地址
String resource = "mybatis/mybatis-config.xml";
//通过IO流 加载指定的配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
//动态生成SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession 类比 数据库链接
SqlSession sqlSession = sqlSessionFactory.openSession();//openSession(true)自动提交事务到数据库
//获取Mapper接口
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
//获取数据
List<DemoUser> userList = demoUserMapper.findAll();
System.out.println(userList);
//关闭链接
sqlSession.close();
}
简化操作
public class TestMybatis {
//定义公共的属性
private SqlSessionFactory sqlSessionFactory;
/**
* mybatis的核心 SqlSessionFacotry对象
* @BeforeEach: 测试API中的注解 在执行@Test注解方法时,会提前执行!!!
*/
@BeforeEach
public void init() throws IOException {
//1.指定资源文件
String resource = "mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testMybatis01(){
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
List<DemoUser> list = demoUserMapper.findAll();
System.out.println(list);
sqlSession.close();
}
}
#号和$符用法
mybatis默认支持单值传参.如果遇到多值,需要将多值转化为单值.
1.利用POJO对象封装
2.利用万能的Map集合
3.如果参数一定使用多值操作,则使用@Param(“sex”) String sex
如果参数采用对象封装,则可以使用#{属性}取值.
如果参数有多个,可以封装为map实现参数的传递. 可以利用#{key}获取数据
也可以使用@Param将多个参数封装为map, 利用#{key}的方式获取数据
使用#{} 获取数据时,默认有预编译的效果.防止sql注入攻击.
mybatis使用#{}获取数据时,默认为数据添加一对""号.
当以字段名称为参数时,一般使用${},但是这样的sql慎用. 可能出现sql注入攻击问题.
Mybatis集合用法
/**
* 例如: 删除id=232/233/234的数据?
* Sql: delete from demo_user where id in (232,233,234)
* 规则: 如果遇到相同的多个数据,则一般采用集合的方式封装数据.
* 封装方式:
* 1. array
* 2. list
* 3. map<List>
*/
@Test
public void testDeleteIds(){
SqlSession sqlSession = sqlSessionFactory.openSession(true);
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
//将数据封装为数组,也可以为List集合,Map集合
int[] ids = {232,233,234};
demoUserMapper.deleteIds(ids);
System.out.println("删除操作成功!!!");
}
接口
<!--
需求: 批量删除多个数据
难点: 如果使用#{集合}获取的是集合对象的整体.删除无效.
思路: 将数组拆分为单个数据. 可以通过遍历的方式操作
语法: mybatis为了参数取值方便,特意封装了遍历的标签 foreach
关于标签参数说明:
<foreach collection=""></foreach>
1.如果传递的参数是数组, 则collection="array"
2.如果传递的参数是list集合, 则collection="list"
3.如果传递的参数是Map集合, 则collection="map中的key"
标签属性说明:
1.collection 集合的名称
2.item 每次遍历的数据的形参变量
3.open 循环的开始标签
4.close 循环的结束标签
5.index 循环遍历下标 一般不用
6.separator 循环遍历的分割符
-->
<delete id="deleteIds">
delete from demo_user where id in (
<foreach collection="array" item="id" separator=",">
#{id}
</foreach>
)
</delete>
动态Sql
IF-WHERE用法
<!--动态Sql案例
思路: 如果数据不为null,mybatis才会当做条件
if标签说明:
test: 判断的条件 直接写属性即可
where标签: 去除条件中多余的 and 或者 or的
说明: if和 where 几乎一起出现.
-->
<select id="findWhere" resultType="DemoUser">
select id,name,age,sex from demo_user
<where>
<if test="name != null">name = #{name}</if>
<if test="age !=null"> and age=#{age}</if>
<if test="sex !=null"> and sex=#{sex}</if>
</where>
</select>
动态Sql-SET标签
<!--
规则: 根据对象中不为null的属性当做set条件
set标签说明: 去除set条件中多余的 ,号
-->
<update id="updateUser">
update demo_user
<set>
<if test="name !=null">name = #{name},</if>
<if test="age !=null">age = #{age},</if>
<if test="sex !=null">sex = #{sex}</if>
</set>
where
id = #{id}
</update>
动态Sql-choose when otherwise
<!--
需求: 如果不想将全部的条件当做if的判断.则mybatis提供了分支结构 switch
语法说明:
choose:代表分支结构,只有一个条件有效.
when: 指定判断的条件 和if类似.
otherwise: 如果上述的条件都不满足时,该行代码有效.
-->
<select id="selectChoose" resultType="DemoUser">
select * from demo_user
where
<choose>
<when test="name !=null">name = #{name}</when>
<otherwise>sex = #{sex}</otherwise>
</choose>
</select>
resultType与resultMap用法
resultType说明:
当结果集中的字段名称,如果与属性的名称一致时,才会实现自动的数据封装
resultMap说明:
当结果集中的字段名称,与对象中的属性不一致时,可以使用resultMap实现自定义的封装.
Mybatis中的缓存机制
一级缓存
概念说明: Mybatis默认开启一级缓存, 一级缓存可以在同一个SqlSession对象中查询相同的数据,可以实现数据的共享(缓存操作).
/**
* Mybatis一级缓存: 默认开启
* 规则: 同一个SqlSession内部有效.
*/
@Test
public void cache1(){
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
List<DemoUser> list1 = demoUserMapper.findAll();
List<DemoUser> list2 =demoUserMapper.findAll();
List<DemoUser> list3 =demoUserMapper.findAll();
System.out.println(list1 == list2);
sqlSession.close();
}
二级缓存
说明: 二级缓存mybatis中默认也是开启的.但是需要手动标识. 二级缓存可以在同一个SqlSessionFactory内部有效.
/**
* 二级缓存说明:
* sqlSession查询数据之后,会将缓存信息保存到一级缓存中.但是不会立即将
* 缓存交给二级缓存保管.如果需要使用二级缓存,则必须将sqlSession业务逻辑执行成功之后关闭.
*/
@Test
public void cache2(){
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
demoUserMapper.findAll();
//关闭一级缓存
sqlSession.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper2 = sqlSession2.getMapper(DemoUserMapper.class);
demoUserMapper2.findAll();
sqlSession2.close();
}