目录
mybatis配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- mybatis 核心配置文件-->
<!-- 导入属性文件 键值对的格式, 配置存放项目的一些参数值-->
<properties resource="config.properties"></properties>
<!-- 配置数据库相关配置,可配置多个-->
<environments default="development">
<environment id="development">
<!-- 事务管理器的配置-->
<transactionManager type="JDBC"/>
<!-- 配置数据源 POOLED是否使用数据库连接池
数据库连接池(缓冲池)
现在每与数据库交互一次,创建一个数据库连接对象(Connection,SqlSession)用完就销毁
下一次需要,重复次过程 频繁的创建销毁对象,需要开销
池:
-->
<dataSource type="POOLED">
<property name="driver" value="${driverName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${uname}"/>
<property name="password" value="${upassword}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/AdminMapper.xml"/>
</mappers>
</configuration>
属性(properties)
<!-- 导入属性文件 键值对的格式, 配置存放项目的一些参数值-->
<properties resource="config.properties"></properties>
在resources中创建 config.properties
driverName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/ssmdb?serverTimezone=Asia/Shanghai
uname=root
upassword=root
环境配置(environments)
transactionManager
<!-- 事务管理器的配置--> <transactionManager type="JDBC"/>
数据库事务
数据库事务:对一次数据库操作过程中,多条执行sql进行管理控制,保证一次执行中的多条sql能够作为一个整体,要么都执行成功,要么都不成功。
jdbc 自动的事务提交,事务提交后就保存到数据库。
打开数据库连接—>第一条sql---->java代码---->第二条sql---->Java代码
---->第三条sql---->关闭连接—提交事务
如果第一次sql执行后,java代码报错,后面的sql不执行,但是第一条sql对数据库进行修改,出问题。
dataSource
<!-- 配置数据源 POOLED是否使用数据库连接池
数据库连接池(缓冲池)
现在每与数据库交互一次,创建一个数据库连接对象(Connection,SqlSession)用完就销毁
下一次需要,重复次过程 频繁的创建销毁对象,需要开销
池:
-->
<dataSource type="POOLED">
设置(settings)
<!--mybaitis 设置-->
<settings>
<!--日志功能-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
类型别名(typeAliases)
<!--配置类型别名-->
<typeAliases>
<!--<typeAlias type="com.ffyc.mybatispro.model.Admin" alias="Admin"></typeAlias>-->
<package name="com.ffyc.mybatispro.model"/>
<!-- 包名 --></typeAliases>
myabtis使用
读取配置文件
Reader reader = Resources.getResourceAsReader(“mybatis-config.xml”);
创建 SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
创建 SqlSession
SqlSession sqlSession = sessionFactory.openSession();
获得接口代理对象
sqlSession.getMapper(接口.class);
sqlSession .close();关闭
public class TestAdmin {
public static void main(String[] args) throws IOException, IOException {
/*
读取mybatis 核心的配置文件
*/
Reader reader= Resources.getResourceAsReader("mybatis.xml");
/*
创建 SqlSessionFacorty
SqlSessionFacorty 封装了所有配置信息
SqlSessionFacorty 负责生成一个数据库连接会话对象 SqlSession对象
SqlSessionFacorty创建开销比较大,所有在整个项目中只需要创建一次,不用销毁
*/
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
/* 创建 SqlSession session会话,一次与数据库交互,类似于之前的Connection
sessionFactory.openSession()方法 用来创建一个 SqlSession对象,
默认无参的设置事务提交为 false 手动提交
*/
SqlSession sqlSession=sessionFactory.openSession();
//创建访问接口的代理对象
AdminDao adminDao=sqlSession.getMapper(AdminDao.class);
//使用代理对象访问对应方法,本质是调用的是接口对应的sql映射文件中那sql
Admin admin=adminDao.findAdminById(1);
System.out.println(admin);
//关闭于数据库连接会话对象
sqlSession.close();
}
}
封装
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.Reader;
public class MybatisUtil {
static SqlSessionFactory sessionFactory;
static {
Reader reader= null;
try {
reader = Resources.getResourceAsReader("mybatis.xml");
} catch (IOException e) {
e.printStackTrace();
}
sessionFactory=new SqlSessionFactoryBuilder().build(reader);
}
public static SqlSession getSqlSession(){
return sessionFactory.openSession();
}
}
封装后使用
public static void main(String[] args) {
SqlSession sqlSession=MybatisUtil.getSqlSession();
AdminDao adminDao=sqlSession.getMapper(AdminDao.class);
Admin admin=adminDao.findAdminById(1);
System.out.println(admin);
//关闭于数据库连接会话对象
sqlSession.close();
}
参数传递
单个参数
我们可直接进行传递
<insert id="saveAdmin" parameterType="string">
insert into admin (account)values (#{account})
</insert>
少量多个参数
在有多个参数时,传入参数可能有多个类型,我们使用一个注解标签解决
void saveAdmin(@Param("acc") String account,@Param("pwd") String password);
<insert id="saveAdmin" >
insert into admin (account,password)values (#{acc},#{pwd})
</insert>
多个参数
有大量参数,使用注解标签就比较麻烦,我们使用对象的形式传入
void saveAdmin1(Admin admin);
<insert id="saveAdmin1" parameterType="Admin"> insert into admin (account,password)values (#{account},#{password})</insert>
#{}与${}区别
#{参数名} 首先是采用预编译方式,一般用于sql传值使用,更加安全
参数名′参数名′{参数名}’ 使用字符串拼接方式传值,不安全,一般用于动态向sql传列名
<!--
'${参数名}' 字符串拼接传值
通过列名进行排序
select * from goods order by ${列名} (列名)
查询特定的列
select ${} ${} ${} from student
-->
新增
立刻拿到id
再新增中,我们可以立即拿到刚插入数据的id
<!--
useGeneratedKeys=true 取出数据库生成的主键
keyColumn=id 告诉mybatis 那个是主键列
keyProperty=id 指定id对应的属性,类中的属性
-->
<insert id="saveAdmin1" parameterType="Admin" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into admin (account,password)values (#{account},#{password})
</insert>
修改
void upadteAdmin(Admin admin);
<update id="upadteAdmin" parameterType="int">
update admin set account=#{account},password=${password} where id=#{id}
</update>
删除
void deleteAdmin(int id);
<delete id="deleteAdmin"> delete from admin where id=#{id}</delete>
结果集处理
返回简单类型
int findAdmincount();
<select id="findAdmincount" resultType="java.lang.Integer">
select count(*) from admin
</select>
对象映射
如果表中的列名,与类中的属性名相同,mybatis会自动将查询结果封装
到POJO对象中。
类中必须要有无参的构造方法,(get set方法 不会直接去用)
public Admin findAdminById(int id);
<select id="findAdminById" resultType="Admin" parameterType="int">
select * from admin where id=#{id}
</select>
也可查询多个
List<Admin> findAdmins();
<select id="findAdminById" resultType="Admin" parameterType="int">
select * from admin where id=#{id}
</select>
查询结果
[Admin{id=1, account='admin', password='111'}, Admin{id=2, account='tom', password='222'}, Admin{id=3, account='jerry', password='333'}, Admin{id=4, account='update', password='0'}]
数据库中使用下划线,Java使用驼峰命名 admin_gender adminGender
不会对对象进行赋值。
得到的值为这样
Admin{id=1, account=‘admin’, password=‘111’, adminGender=‘null’}
1.列名后加 as 修改列名
<select id="findAdminById" resultType="Admin" parameterType="int">
select id,account,password,admin_gender as adminGender from admin where id=#{id}
</select>
2.setting设置 mapUnderscoreToCamelCase 为true
驼峰与下划线 就可转换
<setting name="mapUnderscoreToCamelCase" value="true"/>
特殊处理定义 resultMap
手动处理结果集映射
<!-- 在特殊情况下,我们可以自己进行手动映射
type 最终返回的类型
id 唯一标识
-->
<resultMap id="adminMap" type="Admin">
<!-- 将 数据库中的列,与类中的哪一个属性对应
id标签映射主键
result映射非主键
-->
<result column="gender" property="adminGender"> </result>
</resultMap>
<select id="findAdmins1" resultMap="adminMap"> select id,account,password,admin_gender as gender from admin
</select>
多表关联处理结果集
FULL 使用时会出现,如果查询的有两个id,学生id管理员id,就会将两个id都赋为相同的
<setting name="autoMappingBehavior" value="PARTIAL"/>
嵌套查询1
Student findStudentById(int id);
<resultMap id="studentmap" type="Student">
<id column="id" property="id"></id>
<result column="num" property="num"></result>
<result column="name" property="name"></result>
<result column="gender" property="gender"></result>
<association property="dorm" javaType="Dorm">
<result column="dnum" property="num"></result>
</association>
<association property="admin" javaType="Admin">
<result column="account" property="account"></result>
</association>
</resultMap>
<select id="findStudentById" parameterType="int" resultMap="studentmap">
SELECT
s.id,
s.num,
s.NAME,
s.gender,
d.num dnum,
a.account
from student s LEFT JOIN dorm d on s.dormid=d.id
LEFT JOIN admin a on s.adminid=a.id
where s.id=#{id}
</select>
嵌套查询2
Student findStudentById1(int id);
<resultMap id="studentmap1" type="Student">
<id column="id" property="id"></id>
<result column="num" property="num"></result>
<result column="name" property="name"></result>
<result column="gender" property="gender"></result>
<result column="dormid"></result>
<result column="adminid" ></result>
<association property="dorm" javaType="Dorm" select="findDormById" column="dormid">
</association>
<association property="admin" javaType="Admin" select="findAdminById" column="adminid">
</association>
</resultMap>
<select id="findStudentById1" resultMap="studentmap1" parameterType="int">
select * from student where id=#{id}
</select>
<select id="findDormById" parameterType="int" resultType="Dorm">
select num from dorm where id=#{id}
</select>
<select id="findAdminById" parameterType="int" resultType="Admin">
select account from admin where id=#{id}
</select>
查询结果 再封装
在早些时候,我们在做查询时,例如查询所有宿舍和学生,学生宿舍相同情况下,宿舍号会多次打出,我们使用mybatis可以将一个宿舍的学生封装到一个集合中。
演示
宿舍类—写出如下属性的get set方法,无参构造,toString方法
private int id;
private int num;
private List<Student> students;
宿舍dao接口
List<Dorm> findDorms();
宿舍dao sql映射文件
<resultMap id="dormmap" type="Dorm">
<id column="id" property="id"></id>
<result column="num" property="num"></result>
<!-- 封装学生集合-->
<collection property="students" javaType="list" ofType="student">
<result column="snum" property="num"></result>
<result column="name" property="name"></result>
</collection>
</resultMap>
<select id="findDorms" resultMap="dormmap">
SELECT
d.id,
d.num,
s.name,
s.num snum
FROM dorm d LEFT JOIN student s on d.id=s.dormid
</select>
写法2
<resultMap id="dormmap1" type="Dorm">
<id column="id" property="id"></id>
<result column="num" property="num"></result>
<collection property="students" javaType="list" ofType="student" select="findstudents" column="id">
<result column="num" property="num"></result>
<result column="name" property="name"></result>
<result column="gender" property="gender"></result>
</collection>
</resultMap>
<select id="findDorms1" resultMap="dormmap1">
select * from dorm
</select>
<select id="findstudents" parameterType="int" resultType="student">
select num,name,gender from student where dormid=#{id}
</select>
测试
public static void main(String[] args) {
SqlSession sqlSession= MybatisUtil.getSqlSession();
DormDao dormDao=sqlSession.getMapper(DormDao.class);
List<Dorm> dormList=dormDao.findDorms();
for (Dorm dorm:dormList){
System.out.println("宿舍号"+dorm.getNum());
for(Student student:dorm.getStudents()){
System.out.println(student.getNum()+" "+ student.getName());
}
}
}
结果
宿舍号518
101 张三
宿舍号618
102 李四
103 王博
104 冯采柔
注解
常用注解标签
@Insert : 插入 sql , 和 xml insert sql 语法完全一样
@Select : 查询 sql, 和 xml select sql 语法完全一样
@Update : 更新 sql, 和 xml update sql 语法完全一样
@Delete : 删除 sql, 和 xml delete sql 语法完全一样
@Param
: 入参
@Results : 设置结果集合
@Result : 结果
对于少量sql 我们可以直接使用注解标签的方式
@Delete("delete from student where id=#{id}")
void deleteStudent(int id);
@Insert("insert into student (num,name,gender)values(#{num},#{name},#{gender})")
void saveStudent(Student student);
如果需要手动映射结果集,语句比较多,xml文件中更方便
@Select("select * from student where id=#{id}")
@Results(id="studentmap" ,value = {
@Result(id = true,column = "id",property = "id"),
@Result(column = "num",property = "num")
})
Student findStudent(int id);
@Select(“select * from t_emp”)
@Results(id = “empMap”,value = {
@Result(column = “emp_id”,property = “empId”,id = true),
@Result(column = “emp_name”,property = “empName”),
@Result(column = “emp_tel”,property = “empTel”),
@Result(column = “emp_education”,property = “empEducation”),
@Result(column = “emp_birthday”,property = “empBirthday”)
})
List getAll();
单元测试
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>provided</scope>
</dependency>
在方法前加入 @Test 注解标签就可使用
mybatis动态sql
在进行有条件的查询时候,有时会出现一些问题,例如多条件查询有的条件不存在,有的存在 where前会拼接 and 等等
像这样
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
if where
if 标签可以对传入的条件进行判断
1.where元素会进行判断,如果where包含的标签有返回值,就插入一个where。
2.如果返回值以and或者or开头,则它会剔除掉and或者or
<select id="findStudent" resultType="Student" parameterType="Student">
select num,name,gender from student
<where>
<if test="num!=0">
num=#{num}
</if>
<if test="name!=null">
and name=#{name}
</if>
<if test="gender!=null">
and gender=#{gender}
</if>
</where>
</select>
trim
当 where后紧跟着 and或or的时候 取出and或or
<select id="findStudent" resultType="Student" parameterType="Student">
select num,name,gender from student
<trim prefix="where" prefixOverrides="and|or">
<if test="num!=0">
and num=#{num}
</if>
<if test="name!=null">
and name=#{name}
</if>
<if test="gender!=null">
and gender=#{gender}
</if>
</trim>
</select>
choose
类似与switch,有一个满足条件就跳出,否则为默认、
otherwise 可有可无
<select id="findStudent" resultType="Student" parameterType="Student">
select num,name,gender from student where 1=1
<choose>
<when test="num!=0">
and num=#{num}
</when>
<when test="name!=null">
and name=#{name}
</when>
<otherwise>
and gender="男"
</otherwise>
</choose>
</select>
set
set用于更新,用于动态包含需要更新的列,忽略其它不更新的列。
根据是否有条件成立,添加set 去除最后的 逗号
<update id="updateStudent" parameterType="Student">
update student
<set>
<if test="num!=0">num=#{num},</if>
<if test="name!=null">name=#{name},</if>
<if test="gender!=null">gender=#{gender}</if>
</set>
where id=#{id}
</update>
update student SET name=? where id=?
name后的 逗号去除了
foreach
delete from student where id in (1,2,3)
要查询大量的或批量删除, 可将 id放入 一个数组或者集合中
void deleteStudent(Integer[] array);
<delete id="deleteStudent">
delete from student where id in
<!-- array集合 item 取出的临时属性 类似增强for循环-->
<!-- open separator分隔符 close -->
<foreach collection="array" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</delete>
还可用于查询使用 ${}列名,放到数组/集合 中
select 循环列名数组 from student
特殊符号处理
mybatis中的xml文件中,有一些特殊符号,正常书写mybatis会报错,如 < , >
需要对这些符号进行转义
特殊字符 | 转义字符 |
---|---|
< | < 后面加 ; 分号 |
> | > |
" | " |
’ | &apos |
& | & |
还可使用 用<![CDATA[]]>标签 ,在 CDATA 内部的所有内容都会被解析器忽略。
<select id="findStudent11" resultType="Student" parameterType="int">
select * from student
<where>
<if test="value!=0">
and
<![CDATA[
id<>#{idd}
]]>
</if>
</where>
</select>
数据缓存
缓存是什么
手机上的缓存—刚打开较慢,加载图片, ----存在手机本地
浏览器的缓存— 将一些网页信息,缓存到本地
卖票/抢购缓存— 很多人访问 – 数据缓存(运行内存) --> 数据库
**作用:**让程序更快的访问到数据,同时减少数据库的访问压力,可以将数据缓存 到内存,手机内存,客户端硬盘。存在内存中,当以后再使用时直接从 内存中读取,减小对数据库的查询次数,提高数据库的内存。
mybatis提供一级缓存和二级缓存。
一级缓存
默认开启一级缓存,一级是 SqlSession级别的,当第一次查询数据时,将查到的结果封装到SqlSession对象中,再同一个SqlSession中执行相同的第二次查询时,直接从SqlSession对象中获取。
一级缓存失效:
SqlSession.close关闭
SqlSession.clearCache 清楚缓存数据
执行 新增,修改,删除操作 会修改缓存数据
@Test
public void find(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
StuDao stuDao=sqlSession.getMapper(StuDao.class);
List<Student> list=stuDao.findStudent11(3);
List<Student> list1=stuDao.findStudent11(3);
System.out.println(list);
System.out.println(list);
sqlSession.close();
}
可看出 只执行了一次查询
sqlSession.clearCache();清除缓存
执行两次
二级缓存
Mybatis二级缓存 是SqlSessionFactory级别,将查询数据放到二级缓存中,就可实现多个sqlSession共享,关闭sqlSession 将数据存入到二级缓存中。
配置二级缓存
1.在mybatis中配置
<setting name="cacheEnabled" value="true"/>
2.在pojo类中实现序列化接口 java.io.Serializable
public class Student implements Serializable {
private int id;
private int num;
private String name;
private String gender;
3.再mapper映射文件中 添加 表示此mapper开启二级缓存
<mapper namespace="com.ffyc.mybatispro.dao.StuDao">
<!--开启二级缓存-->
<cache></cache>
测试
@Test
public void find1(){
SqlSession sqlSession1= MybatisUtil.getSqlSession();
StuDao stuDao1=sqlSession1.getMapper(StuDao.class);
List<Student> list=stuDao1.findStudent11(3);
sqlSession1.close();
System.out.println(list);
SqlSession sqlSession2= MybatisUtil.getSqlSession();
StuDao stuDao2=sqlSession2.getMapper(StuDao.class);
List<Student> list2=stuDao2.findStudent11(3);
sqlSession2.close();
System.out.println(list2);
}
当 SqlSeesion 关闭时,会将数据存入到二级缓存
从控制台信息可以看到只执行了一次查询
报错
意思大概就是: 在“类java.lang.Integer”中没有名为“*”的属性的getter。
There is no getter for property named 'id' in 'class java.lang.Integer'
在使用mybatis执行下面语句是 发送错误
<select id="findStudent11" resultType="Student" parameterType="int">
select * from student
<where>
<if test="id!=0">
and
<![CDATA[
id<>#{id}
]]>
</if>
</where>
</select>
解决 对test属性进行设置 或者在dao层添加@Param注解
<if test="id!=0"> 改为
<if test="_parameter!=0">或者
<if test="value!=0">
解决原因
- sql在test条件中使用传递参数要放在实体对象中,不让报错找不到对象
- 如果不放在实体对象中传输,就在test对比中使用_parameter或者value
- _parameter,value代表传进来的参数,如果传进来的参数是多个,那个parameter代表get(0)第一个