1、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>
<properties resource="db.properties"></properties>
<typeAliases>
<!--<typeAlias alias="user" type="com.kuang.bean.User"></typeAlias>-->
<package name="com.kuang.bean"></package>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="1114"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/kuang/dao/UserMapper.xml"/>
</mappers>
</configuration>
1.1 标签
-
表示使用数据库连接 -
<properties resource="db.properties"></properties> 表示引入外部的配置文件
-
<typeAliases>
<!--<typeAlias alias="user" type="com.kuang.bean.User"></typeAlias>-->
<package name="com.kuang.bean"></package>
</typeAliases>
表示该类可以使用别名
-
mappers
作用:扫包,为mapper接口创建实现类
方式一 <mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
可以将mapper.xml放在dao或者resource包下 方式二 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Yv6asuB-1638366132989)(C:\Users\yyp\AppData\Roaming\Typora\typora-user-images\image-20211110145129894.png)] <mappers>
<mapper class="com.kuang.dao.UserMapper"></mapper>
</mappers>
dao的命名要和mapper.xml的命名一致,并且放到一个包下面 方式三 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ktgAOSLf-1638366132992)(C:\Users\yyp\AppData\Roaming\Typora\typora-user-images\image-20211110145129894.png)] <mappers>
<package name="com.kuang.dao"></package>
</mappers>
dao的命名要和mapper.xml的命名一致,并且放到一个包下面
1.2 mapper.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.kuang.dao.DeptMapper">
</mapper>
1.3 添加日志设置
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
1.4 dataBase
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=1114
2、生命周期作用域
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p7cHMDp6-1638366132994)(C:\Users\yyp\AppData\Roaming\Typora\typora-user-images\image-20211110150909249.png)]
- SqlSessionFactoryBuilder 创建完了SqlSessionFactory就失去作用,所以它作为一个局部变量即可
- SqlSessionFactory 可以被看做一个数据库连接池,作用是创建SqlSession,要长期保存它,但是不能存在多个Factory,单个便于对资源的管控,所以它最好作为单例模式
- SqlSession 就相当于一个数据库连接,执行CRUD,事务的提交或回顾,所以它的作用域是一个方法中
3、resultMap
<resultMap id="UserMap" type="User">
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<result column="pwd" property="password"></result>
</resultMap>
<select id="getUserById" resultMap="UserMap">
SELECT * from user where id = #{id}
</select>
标签:
? id:是该resultMap的一个标志,可以被其他resultMap使用
? type:表示返回类型
?
?
? id:表示这是主键列,会有特殊的封装规则
? result:表示这是普通列
? column:对应表中的列名
? property:表示javaBean中对应的属性名
说明:id 和 column就是表中列名或者列的别名
3.1 外键
- 特点:一致性,完整性
- 两个表:A:学生(学号,姓名) B:学生成绩(学号,成绩);将表A的学号作为表B的外键
- 想要在表B中插入某个学号的成绩,那么在表A中必须要有该学号;此时数据库对插入的数据做了关系检查
- 想要删除表A中某个学生记录,必须确保表B中没有引用表A中的字段(学号);此时数据库对插入的数据做了关系检查
CREATE TABLE tbl_student(
id INT(10) PRIMARY KEY,
NAME VARCHAR(20)
);
CREATE TABLE tbl_stu_score(
math VARCHAR(10),
english VARCHAR(10),
stu_id INT(10),
CONSTRAINT fk_stu_score FOREIGN KEY(stu_id)
REFERENCES tbl_student(id)
);
INSERT INTO tbl_student VALUES(1,"张三"),
(0002,"李四");
INSERT INTO tbl_stu_score VALUES
(100,100,1),
(99,99,2)
INSERT INTO tbl_stu_score VALUES(98,98,3)
#报错,因为没有0003这个学号
DELETE FROM tbl_student WHERE id = 1
#报错,无法删除,因为学生成绩表中引用了学生表中的id字段
#正确的删除方式
#首先删除成绩表
DELETE FROM tbl_stu_score WHERE stu_id = 1
#再删除学生表
DELETE FROM tbl_student WHERE id = 1
3.1.2 给已有的列添加外键
ALTER TABLE tbl_emp add CONSTRAINT emp_dept_fk FOREIGN KEY (d_id)
REFERENCES tbl_dept(dept_id)
ALTER TABLE 子表 ADD CONSTRAINT 外键名 FOREIGN KEY (关联字段)
REFERENCES 主表(被关联的字段) on delete cascade on update restrict;
3.2连接查询
3.2.1 把列值直接封装到bean中
<resultMap id="getDifEmp" type="Employee">
<id column="id" property="id"></id>
<result column="last_name" property="lastName"></result>
<result column="gender" property="gender"></result>
<result column="email" property="email"></result>
<result column="d_id" property="dept.id"></result>
<result column="dept_name" property="dept.deptName"></result>
</resultMap>
<!--public Employee getEmployeeById(Integer id);-->
<select id="getEmployeeById" resultMap="getDifEmp">
SELECT e.`id`, e.`last_name`, e.`gender`, e.`email`, e.`d_id`,
d.`dept_name`
FROM tbl_employee e
INNER JOIN tbl_dept d ON e.`d_id` = d.`id`
WHERE e.id = #{id}
</select>
3.2.2 association联合查询(1v1)
- 在javaBean中含有一个bean对象的属性的时候封装使用
<resultMap id="getDifEmp2" type="Employee">
<id column="id" property="id"></id>
<result column="last_name" property="lastName"></result>
<result column="gender" property="gender"></result>
<result column="email" property="email"></result>
<association property="dept" javaType="Dept">
<id column="id" property="id"></id>
<result column="dept_name" property="deptName"></result>
</association>
</resultMap>
?
? association:表示该bean中有联合了一个bean类型的属性,现在要自定义封装该联合对象
? property:javaBean中联合的那个属性的属性名
? javaType:该bean对应的类型
注意:我们使用联合查询的时候,要注意sql语句,同时要把联合的那个bean对象所在表的数据查询出来,这样才能有数据且成功赋值到这个bean所在的属性
<select id="queryAllEmps" resultMap="EmpAndDeptLimit">
SELECT e.emp_id,e.emp_name,e.gender,e.email,d.dept_id,d.dept_name
FROM tbl_emp e
LEFT JOIN tbl_dept d ON e.d_id = d.dept_id
</select>
3.2.3 association分步查询(1v1)
- 就是先查出表1,由于表1含有外键,这个外键来自表2;所以表2可以根据该外键查询出来
- 最后封装起来返回一个对象
要查询出Emp对象
Emp中含有d_id,它来自于Dept中;
第一步查询出Emp的信息,不管是否要查询出d_id来封装,都会查询到d_id;
第二步根据d_id来查询出对应的Dept对象;最后封装成一个Emp对象返回
<resultMap id="getStepEmp" type="Employee">
<id column="id" property="id"></id>
<result column="last_name" property="lastName"></result>
<result column="gender" property="gender"></result>
<result column="email" property="email"></result>
<association property="dept" select="com.kuang.dao.DeptMapper.getDeptById" column="d_id"></association>
</resultMap>
<!-- public Employee getEmpByStep(Integer id);-->
<select id="getEmpByStep" resultMap="getStepEmp">
SELECT * from tbl_employee where id = #{id}
</select>
?
? property:表示联合的bean对象
? select:当前property是调用select中指定的方法查询出来的
? column:表示要将第一次查询出的哪个属性值传递到第二次select中的sql参数中
分步中的延迟加载
- 配置以下两个属性,那么数据库查询出的结果就会按需加载,可以是数据库查询更优化
<settings>
<setting name="lazyLoadingEnabled" value="true"></setting>
<setting name="aggressiveLazyLoading" value="false"></setting>
</settings>
举例
@Test
public void testStep(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmpByStep(6);
System.out.println(emp.getLastName());
sqlSession.close();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pRZW8yEm-1638366132996)(C:\Users\yyp\AppData\Roaming\Typora\typora-user-images\image-20211111103938149.png)]
- 可以看到只发送了一次sql语句,根据查询需求,数据库没有去查询dept属性
3.3.4 collection(1v多)
- 在javaBean的属性中含有Collection(List、Set)类型的时候封装使用
查询一个部门中所有的Emps
<resultMap id="getList" type="Dept">
<id column="id" property="id"></id>
<result column="dept_name" property="deptName"></result>
<collection property="emps" ofType="Employee">
<id column="id" property="id"></id>
<result column="last_name" property="lastName"></result>
<result column="gender" property="gender"></result>
<result column="email" property="email"></result>
</collection>
</resultMap>
<!--public Dept getDeptAndListById(Integer id);-->
<select id="getDeptAndListById" resultMap="getList">
SELECT d.`id`, d.`dept_name` ,
e.`id`, e.`last_name`, e.`gender`, e.`email`
FROM tbl_dept d
LEFT JOIN tbl_employee e ON d.`id` = e.`d_id`
WHERE d.`id` = #{id}
</select>
collection:表示联合的属性是一个集合
property:表示联合的属性名
ofType:表示联合的属性的类型
3.3.5 collection分步查询
<resultMap id="getListStep" type="Dept">
<id column="id" property="id"></id>
<result column="dept_name" property="deptName"></result>
<collection property="emps" select="com.kuang.dao.EmployeeMapper.getEmployeeBydid" column="id"></collection>
</resultMap>
<!--public Dept getDeptAndListByStep(Integer id);-->
<select id="getDeptAndListByStep" resultMap="getListStep">
select * from tbl_dept where id = #{id};
</select>
3.3.6 分步查询的多值传递
<resultMap id="getListStep" type="Dept">
<id column="id" property="id"></id>
<result column="dept_name" property="deptName"></result>
<!--public List<Employee> getEmployeeBydid(Integer did);-->
<collection property="emps"
select="com.kuang.dao.EmployeeMapper.getEmployeeBydid"
column="{did = id}"></collection>
</resultMap>
<!--public Dept getDeptAndListByStep(Integer id);-->
<select id="getDeptAndListByStep" resultMap="getListStep">
select * from tbl_dept where id = #{id};
</select>
- select中调用的方法,传参的时候使用 column="{key1 = value1, key2 = value2}" 的方式
3.3.7鉴别器
<resultMap id="getEmpDisc" type="Employee">
<id column="id" property="id"></id>
<result column="last_name" property="lastName"></result>
<result column="gender" property="gender"></result>
<result column="email" property="email"></result>
<discriminator javaType="string" column="gender">
<!--如果是女生-->
<case value="0" resultType="com.kuang.bean.Employee">
<association property="dept"
select="com.kuang.dao.DeptMapper.getDeptById"
column="d_id"></association>
</case>
<!--如果是男生-->
<case value="1" resultType="com.kuang.bean.Employee">
<id column="id" property="id"></id>
<result column="last_name" property="lastName"></result>
<result column="gender" property="gender"></result>
<result column="last_name" property="email"></result>
</case>
</discriminator>
</resultMap>
?
? column:表示要判断的是哪一列
? javaType:表示列值的类型
?
? value:表示列值
? resultType:表示将case包围的该对象返回给谁
4、分页查询
可以降低数据库查询压力,将大量的查询,分成小部分来查询
5、方法中的参数处理
5.1、单个参数
<!-- public User getUserById(String id); -->
<select id="getUserById" resultMap="UserMap">
SELECT * from user where id = #{id2312312sdfsadf}
</select>
- 对于含有单个参数的方法,Mybatis不做处理,#{},大括号内可以写任意值,sql中传入的都是方法中的参数
5.2、多个参数
#{}直接传参数名会报错:Parameter ‘id’ not found. Available parameters are [arg1, arg0, param1, param2]
5.2.1显示的多个参数
-
对于方法中传入多个参数的情况,参数都会被封装到一个map中,sql中可以通过#{key}的形式来取参数value; 相当于去map中取值 -
可以用param1…paramN来指定sql中待传入的参数,也可以用arg0,arg1来指定
key : param1…paramN arg0…argN
value : 方法中传入的值
例子
<!--public User getUserByTw(Integer id,String name);-->
<select id="getUserByTw" resultType="User">
SELECT * from user where id = #{arg0} and name = #{arg1}
</select>
- 还可以用注解@param来为方法中的参数命名,那么sql语句中待传入的参数值就是@param()括号中的值
key : @param(“ ”) 括号中的命名
value :方法中传入的值
方法:
public User getUserByTw(@Param("id") Integer id, @Param("name") String name);
xml配置:
<!--public User getUserByTw(@Param("id") Integer id, @Param("name") String name);-->
<select id="getUserByTw" resultType="User">
SELECT * from user where id = #{param1} and name = #{param2}
</select>
5.2.2 隐式的多个参数
比如方法中形参是pojo/Map/Collection,其实这时候方法中传进来也是多个参数,但是在方法中又只体现传入一个值
此时的key就是直接对应于pojo/map中的key
- 如果要传入的值在pojo中,可以在方法中直接传入一个pojo对象
- 如果要传入的值不在pojo中,可以在方法中传递一个map对象,相当于去map中取值
举例
mapper
public User getUserByMap(Map<String,Object> map);
(mapper.xml)
<!-- public User getUserByMap(Map<String,Object> map);-->
<select id="getUserByMap" resultType="User">
SELECT * from #{tableName} where id = #{id} and name = #{name}
</select>
(测试类)
@Test
public void testGetByMap() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("id",1);
map.put("name","狂神");
map.put("tableName","user")
User userByMap = mapper.getUserByMap(map);
System.out.println(userByMap);
}
}
5.2.3特殊
public User getUser(@Param("id") Integer id,@Param("user") User u);
取值:id ===> #{id} name ===> #{user.name}
举例:方法中传入List
public User getUser(List<User> users)
取值:取第一个值 #{list[0]}
6、#{}和${}的区别
<!-- public User getUserById(String id); -->
<select id="getUserById" resultType="User">
SELECT * from user where id = ${id}
</select>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DW0AdOuI-1638366132998)(C:\Users\yyp\AppData\Roaming\Typora\typora-user-images\image-20211110204627536.png)]
- 大多数情况使用#{}的方式取值,但有时候会用到${}的方式取值,比如说要拼接sql语句中的表名时(上面的5.2.2中是一个例子)
- 还有就是使用order by的时候
select * from ${tableName} where id = #{id}
select * from user order by ${name}
7、动态sql语句
7.1 if
<!--public List<Blog> getBlobIf(Map map);-->
<select id="getBlobIf" resultType="Blog">
SELECT * from mybatis.blog
<where>
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and autore = #{author}
</if>
</where>
</select>
表示:where后面的语句如果有多余的and/or,会将其自动去除;如果where后面没有语句,则将where自动去除
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
trim标签这样使用效果和where的效果相同
7.2 choose、when
<!--public List<Blog> getBlobSwitch(Map map);-->
<select id="getBlobChoose" resultType="Blog">
SELECT * from mybatis.blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
views = #{views}
</otherwise>
</choose>
</where>
</select>
- 如同 switch-case,如果前面的when中有一个满足,则不会再去执行下面的匹配
7.3 set
<!--public int updateBlob(Map map);-->
<update id="updateBlob">
UPDATE mybatis.blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != author">
author = #{author},
</if>
</set>
where id = #{id}
</update>
:会自动去掉set后面的逗号
<trim prefix="SET" suffixOverrides=",">
...
<trim>
trim在这里使用的效果和set相同
7.4 提取sql公共片段
<sql id="if-title-author">
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and autore = #{author}
</if>
</sql>
- 在mapper.xml中有公共的片段,可以将其提取出来,然后在sql中使用
7.5 for-each
<!--select * from mybatis.blog where (id = 1 or id=2 or id=3 );-->
<!--public List<Blog> getBlobForEach(Map map);-->
<select id="getBlobForEach" resultType="Blog">
SELECT * from mybatis.blog
<where>
<foreach collection="ids" item="id" index="index"
open="(" separator="or" close=")">
id = #{id}
</foreach>
</where>
</select>
collection:表示map中的一个元素(在这里是一个集合)
item:表示集合中的每一项元素
如果不是最后一项,会使用separator="or"进行拼接,否则使用close结束
8、缓存
8.1 什么是缓存
-
存在内存中的临时数据 -
数据库的查询会占用资源,需要使用缓存来解决这个问题 -
默认情况,只有一级缓存开启,就是在一个sqlSession中 -
二级缓存是需要手动开启,它可以在一个namespace(mapper接口)方法中有效
8.2 一级缓存
-
在同一个sqlSession中有效 -
一级缓存默认开启,无法关闭 -
失效的几种情况
- 进行了增删改
- 不是同一次sqlSession
- 查询的不是同一个东西
- 手动清理了缓存
8.3 二级缓存
<settings>
<setting name="cacheEnabled" value="true"></setting>
</settings>
在xml中开启二级缓存
<cache/>
在对应的mapper中开启二级缓存
- 作用域:可作用于同一个namespace中
- 所有的数据都会先放在一级缓存中
- 只有会话关闭,才会将数据提交到二级缓存中
- 如果有两个会话,会话1查询数据后关闭连接,会话2查询相同的数据才能使用二级缓存
8.4 缓存执行原理
- 先去二级缓存中查找,再去一级缓存中查找,再去数据库中查找
9、mapper.ml文件无法被maven加载到target文件中的问题
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
|