mybatis:原来是apache的一个开源项目,叫ibatis。2010年转移谷歌,从3.0开始改名为mybatis
mybatis是一款优秀的持久层框架,是对jdbc功能进行轻量级的封装,提供了统一的数据库信息配置统一放在一个xml文件中,读取就行。将sql提取到一个xml文件中,提供了动态sql功能,提供了结果自动映射封装。
是一个orm(ORM Object Relational Mapping 对象关系映射)实现,orm指的是,将数据中的记录与java中的对象进行关系映射。
对jdbc原生接口进行封装,提供了一些mybatis自己的接口和类来实现
MyBatis 环境搭建
1.创建一个maven项目, 把项目添加到git仓库
2.创建数据库表,创建对应的类
create database ssmdb charset utf8
create table admin(
id int primary key auto_increment,
account varchar(30),
password varchar(50)
)
package com.ffyc.mybatisPro.model;
public class Admin {
private int id;
private String account;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "Admin{" +
"id=" + id +
", account='" + account + '\'' +
", password='" + password + '\'' +
'}';
}
}
3.添加mybatis相关的依赖 mybatis、mysql
<dependencies>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
</dependencies>
4.创建一个mybatis的核心配置文件
- 数据库连接部分信息
- sql映射文件
<!--自己用的配置文件在resources,New/File下创建一个文件mybatis_config.xml-->
<?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>
<properties resource="config.properties"></properties> <!--导入配置属性文件-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/> <!--数据事务管理配置-->
<dataSource type="POOLED"> <!--数据源配置,type="POOLED"使用数据库连接池-->
<property name="driver" value="${classDriverName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${uname}"/>
<property name="password" value="${pwd}"/>
</dataSource>
</environment>
<!-- <environment id="work">
</environment>-->
</environments>
<mappers> <!--配置SQL映射文件-->
<mapper resource="mappers/AdminMapper.xml"/>
</mappers>
</configuration>
//在resources/config.properties中
classDriverName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/ssmdb?serverTimezone=Asia/Shanghai
uname=root
pwd=123456
5.开发了接口
package com.ffyc.mybatisPro.dao;
import com.ffyc.mybatisPro.model.Admin;
//通过id,查询之后返回Admin对象
public interface AdminDao {
Admin findAdminById(int id);
}
6.创建了sql映射文件
/*在resources/mappers/adminMapper.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.ffyc.mybatisPro.dao.AdminDao"> <!--和接口名字一致-->
<select id="findAdminById" parameterType="int" resultType="Admin"> /*可以直接写Admin*/
select id,account,password from admin where id=#{id}
</select>
</mapper>
MybatisX插件,可以切换接口与SQL映射
7.测试
SqLSessionFactory用来封装配置信息,主要用来创建SqLSession对象。由于SqLSessionFactory对象创建开销较大,所以一个项目中只创建一个,不用关闭
SqLSession用来每次与数据库会话使用的,每次与数据库交互一次都需要创建一个独立的sqlSession
package com.ffyc.mybatisPro.test;
public class Test {
public static void main(String[] args) throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis_config.xml");//读取配置文件
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);//创建SqlSessionFactory
SqlSession sqlSession = sessionFactory.openSession();//创建SqlSession
//AdminDao.class(拿到类被加载到内存中的CLass对象),getMapper创建了接口的代理对象
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin = adminDao.findAdminById(1);//调用接口中方法 对应的sql
System.out.println(admin);
sqlSession.close();//关闭了sqlSession,不用关闭sessionFactory
}
}
xml配置
配置类型简称
<typeAliases> <!--配置类型简称-->
<!--<typeAlias type="com.ffyc.mybatisPro.model" alias="Admin"></typeAlias>-->
<package name="com.ffyc.mybatisPro.model"/>
</typeAliases>
<?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.ffyc.mybatisPro.dao.AdminDao">
<select id="findAdminById" parameterType="int" resultType="Admin"> /*可以直接写Admin*/
select id,account,password from admin where id=#{id}
</select>
</mapper>
常用设置
<settings> <!--常用设置-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
junit单元测试
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>provided</scope>
</dependency>
package com.ffyc.mybatisPro.test;
public class Test1 {
@Test
public void find(){
System.out.println("aaa");
}
@Test
public void save(){
System.out.println("bbb");
}
}
对Test代码进行封装
package com.ffyc.mybatisPro.util;
public class MybatisUtil {
static SqlSessionFactory sessionFactory;
//静态代码块,在类加载时执行一次
static {
Reader reader = null;
try {
reader = Resources.getResourceAsReader("mybatis_config.xml");
} catch (IOException e) {
e.printStackTrace();
}
sessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
//创建并返回SqlSession
public static SqlSession getSqlSession(){
return sessionFactory.openSession();
}
}
public class Test1 {
@Test
public void find(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin = adminDao.findAdminById(1);//调用接口中方法 对应的sql
System.out.println(admin);
sqlSession.close();
}
}
参数传递
1.基本类型的单个参数,直接传递
public interface AdminDao {
Admin findAdminById(int id);
}
<mapper namespace="com.ffyc.mybatisPro.dao.AdminDao"> <!--和接口名字一致-->
<select id="findAdminById" parameterType="int" resultType="com.ffyc.mybatisPro.model.Admin"> /*可以直接写Admin*/
select id,account,password from admin where id=#{id}
</select>
</mapper>
public class Test1 {
@Test
public void find(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin = adminDao.findAdminById(1);//调用接口中方法 对应的sql
System.out.println(admin);
sqlSession.close();
}
}
2.多个参数传递
方法一:多个参数、或引用类型使用注解标签@Param绑定
public interface AdminDao {
Admin login(@Param("acc")String account, @Param("pwd") String password);
}
<mapper namespace="com.ffyc.mybatisPro.dao.AdminDao"> <!--和接口名字一致-->
<select id="login" resultType="com.ffyc.mybatisPro.model.Admin">
select * from admin where account=#{acc} and password=#{pwd}
</select>
</mapper>
public class Test1 {
@Test
public void login(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin = adminDao.login("admin","admin");//调用接口中方法 对应的sql
System.out.println(admin);
sqlSession.close();
}
}
方法二:多个参数封装到一个对象中(推荐)
public interface AdminDao {
Admin login2(Admin admin);
}
<mapper namespace="com.ffyc.mybatisPro.dao.AdminDao"> <!--和接口名字一致-->
<select id="login2" parameterType="com.ffyc.mybatisPro.model.Admin" resultType="com.ffyc.mybatisPro.model.Admin">
select * from admin where account=#{account} and password=#{password} /*model里面的属性*/
</select>
</mapper>
public class Test1 {
@Test
public void login2(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin=new Admin();
admin.setAccount("admin");
admin.setPassword("admin");
Admin a= adminDao.login2(admin);//调用接口中方法 对应的sql
System.out.println(a);
sqlSession.close();
}
}
3.字符串类型单独传递时,需要进行绑定处理
SQL中接收参数:#{} 和${}区别(面试必问)
- #{参数名} 占位符,预编译方式传值,编译好 SQL 语句再取值,#方式能够防止 sql 注入,更安全
select * from admin where account=#{account} and password=#{password}
-
参数名拼接符,主要用来动态的向
s
q
l
中传列名;传值时是直接将参数拼接到
s
q
l
中
(
不建议
)
,需要加单引号,会传入参数字符串,取值以后再去编译
S
Q
L
语句,无法防止
S
q
l
注入
s
e
l
e
c
t
∗
f
r
o
m
a
d
m
i
n
w
h
e
r
e
a
c
c
o
u
n
t
=
′
¥
a
c
c
o
u
n
t
′
a
n
d
p
a
s
s
w
o
r
d
=
′
{参数名} 拼接符,主要用来动态的向sql中传列名;传值时是直接将参数拼接到sql中(不建议),需要加单引号,会传入参数字符串,取值以后再去编译 SQL 语句,无法防止 Sql 注入 select * from admin where account='¥{account}' and password='
参数名拼接符,主要用来动态的向sql中传列名;传值时是直接将参数拼接到sql中(不建议),需要加单引号,会传入参数字符串,取值以后再去编译SQL语句,无法防止Sql注入select∗fromadminwhereaccount=′¥account′andpassword=′{password}’
select * from admin where ${column}=‘admin’ - 注意:MyBatis 排序时使用 order by 动态参数时需要注意,用$而不是#
select * from admin order by ${column} 动态传列名
public interface AdminDao {
Admin find2(@Param("column")String column);
}
<select id="find2" parameterType="String" resultType="com.ffyc.mybatisPro.model.Admin">
select * from admin where ${column}='admin'
</select>
@Test
public void find2(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin = adminDao.find2("account");//调用接口中方法 对应的sql
System.out.println(admin);
sqlSession.close();
}
新增
public interface AdminDao {
void saveAdmin(Admin admin);
}
<insert id="saveAdmin" parameterType="com.ffyc.mybatisPro.model.Admin">
insert into admin(account,password) values (#{account},#{password})
</insert>
事务: 我们每次对数据库连接上,发送sql到执行完,数据库有一个机制进行控制开启事务
做一个转账操作
sql1 -100
java… 异常
sql2 +100
java…
提交事务:当我们做完所有的事情之后,提交事务,数据库真正的执行
jdbc默认是自动提交事务;mybatis把自动事务提交关闭了:Setting autocommit to false,需要在所有的业务代码执行成功后,手动提交事务
@Test
public void save(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin=new Admin();
admin.setAccount("zhangsan");
admin.setPassword("zhangsan");
adminDao.saveAdmin(admin);//调用接口中方法 对应的sql
sqlSession.commit();//提交事务;新增,修改,删除操作尽量都在事务控制中进行
sqlSession.close();
}
保存管理员后,立即拿到id
开启将生成的主键列,自动封装到对象中
<!--keyProperty="id"是类中的属性名,keyColumn="id"是数据库中的列名;把数据库生成的主键拿到赋给Admin对象中的id属性-->
<insert id="saveAdmin" parameterType="com.ffyc.mybatisPro.model.Admin" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into admin(account,password) values (#{account},#{password})
</insert>
@Test
public void save(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin=new Admin();
admin.setAccount("zhangsan");
admin.setPassword("zhangsan");
adminDao.saveAdmin(admin);//调用接口中方法 对应的sql
System.out.println(admin.getId());
sqlSession.commit();//提交事务;新增,修改,删除操作尽量都在事务控制中进行
sqlSession.close();
}
修改
public interface AdminDao {
void updateAdmin(Admin admin);
}
<update id="updateAdmin" parameterType="com.ffyc.mybatisPro.model.Admin">
update admin set account=#{account},password=#{password} where id=#{id}
</update>
@Test
public void update(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin=new Admin();
admin.setId(4);
admin.setAccount("lisi");
admin.setPassword("lisi");
adminDao.updateAdmin(admin);//调用接口中方法 对应的sql
sqlSession.commit();//提交事务;新增,修改,删除操作尽量都在事务控制中进行
sqlSession.close();
}
删除,通过id删除管理员
public interface AdminDao {
void deleteAdminById(int id);
}
<delete id="deleteAdminById" parameterType="int">
delete from admin where id=#{id}
</delete>
@Test
public void delete(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
adminDao.deleteAdminById(4);//调用接口中方法 对应的sql
sqlSession.commit();//提交事务;新增,修改,删除操作尽量都在事务控制中进行
sqlSession.close();
}
查询
1、单张表查询
1.查询管理员的总数
public interface AdminDao {
//查询管理员的总数
int adminCount();
}
<select id="adminCount" resultType="int">
select count(*) from admin
</select>
@Test
public void find(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
int count=adminDao.adminCount();//调用接口中方法 对应的sql
System.out.println(count);
sqlSession.close();
}
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN映射到经典 Java 属性名 aColumn。 | true | false | False |
2.通过id查询
public interface AdminDao {
Admin findAdminById(int id);
}
mybatis会将查询到的结果自动封装到一个对象中,会自己创建给定类型的类的对象
自动封装结果的条件:
- 开启了全局的自动结果映射,PARTIAL默认是单张表开启的
- 数据库列名与属性名名字相同;如果名字不一致,不能映射,可以定义别名使其一致
- mapUnderscoreToCamelCase = true;数据库中admin_gender可以映射到类中的adminGender
<!--mybatis会将查询到的结果自动封装到一个对象中,会自己创建给定类型的类的对象-->
<select id="findAdminById" parameterType="int" resultType="com.ffyc.mybatisPro.model.Admin">
select id,account,password,admin_gender from admin where id=#{id}
</select>
@Test
public void find2(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin=adminDao.findAdminById(1);//调用接口中方法 对应的sql
System.out.println(admin);
sqlSession.close();
}
<settings> <!--常用设置-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
3.查询多个管理员,先创建对象然后自动装入集合中
public interface AdminDao {
List<Admin> findAdmins();
}
<select id="findAdmins" resultType="com.ffyc.mybatisPro.model.Admin">
select id,account,password,admin_gender from admin
</select>
@Test
public void find3(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
List<Admin> list=adminDao.findAdmins();//调用接口中方法 对应的sql
System.out.println(list);
sqlSession.close();
}
<settings> <!--常用设置-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
在resultMap标签中,可以进行自定义的结果映射
column="admin_gender"是数据库中的, property="gender"是类中的
<!--在resultMap标签中,可以进行自定义的结果映射-->
<resultMap id="adminMap" type="com.ffyc.mybatisPro.model.Admin">
<id column="id" property="id"></id>
<result column="account" property="account"></result>
<result column="password" property="password"></result>
<result column="admin_gender" property="gender"></result>
</resultMap>
<select id="findAdmins" resultType="com.ffyc.mybatisPro.model.Admin" resultMap="adminMap">
select id,account,password,admin_gender from admin
</select>
2、多张表查询
2.1关联查询
1.通过id查询学生
数据库
create table student(
id int primary key auto_increment,
num int,
name varchar(10),
gender char(1),
birthday date,
dormid int,
adminid int,
oper_time datetime
)
create table dorm(
id int primary key auto_increment,
num int
)
注意:
建议模型类中的属性基本类型都使用包装类型,因为引用类型默认值为null
一旦类中定义了有参构造方法,一定要把无参构造方法显示的定义出来,因为框架需要使用它
java中可以进行方法重载,mybatis中不行,因为.xml是通过id(接口的方法名)区分的
package com.ffyc.mybatisPro.model;
public class Student {
private Integer id;//建议使用引用类型,默认值为null
private Integer num;
private String name;
private String gender;
private Date birthday;
private Dorm dorm;//关联关系,一个类关联另一个类
private Admin admin;
private Date operTime;
//get和set方法
}
package com.ffyc.mybatisPro.model;
public class Dorm {
private int id;
private int num;//宿舍号
private Admin admin;
private List<Student> students;//一个宿舍对应多个学生
public Admin getAdmin() {
return admin;
}
public void setAdmin(Admin admin) {
this.admin = admin;
}
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
@Override
public String toString() {
return "Dorm{" +
"id=" + id +
", num=" + num +
'}';
}
}
public interface StudentDao {
Student findStudentById(int id);
}
<?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.ffyc.mybatisPro.dao.StudentDao">
<resultMap id="studentMap" type="com.ffyc.mybatisPro.model.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="birthday" property="birthday"></result>
<result column="oper_time" property="operTime"></result>
<!-- 用来封装关联的对象信息,property="dorm",就会创建一个关联的对象-->
<!--column="dormNum"是学生中的, property="num"是dorm中的-->
<association property="dorm" javaType="com.ffyc.mybatisPro.model.Dorm">
<result column="dormNum" property="num"></result>
</association>
<association property="admin" javaType="com.ffyc.mybatisPro.model.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,
s.birthday,
d.num dormNum,
a.account,
s.oper_time
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>
</mapper>
public class TestStudent {
@Test
public void find(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student =studentDao.findStudentById(1);//调用接口中方法 对应的sql
System.out.println(student.getNum());//拿到学号
System.out.println(student.getDorm().getNum());//拿到宿舍号
sqlSession.close();
}
}
mybatis默认配置是:一旦出现了套关联查询,就不自动映射,可以开启(可以映射的都赋值);如果在resultMap标签中进行自定义的结果映射就不需要这个(把指定的赋值)
<settings> <!--常用设置-->
<setting name="autoMappingBehavior" value="FULL"/>
</settings>
2.查询多个学生
public interface StudentDao {
List<Student> findStudents();
}
<resultMap id="studentMap" type="com.ffyc.mybatisPro.model.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="birthday" property="birthday"></result>
<result column="oper_time" property="operTime"></result>
<!-- 用来封装关联的对象信息,property="dorm",就会创建一个关联的对象-->
<!--column="dormNum"是学生中的, property="num"是dorm中的-->
<association property="dorm" javaType="com.ffyc.mybatisPro.model.Dorm">
<result column="dormNum" property="num"></result>
</association>
<association property="admin" javaType="com.ffyc.mybatisPro.model.Admin">
<result column="account" property="account"></result>
</association>
</resultMap>
<select id="findStudents" resultMap="studentMap">
select
s.id,
s.num,
s.name,
s.gender,
s.birthday,
d.num dormNum,
a.account,
s.oper_time
from student s left join dorm d on s.dormid=d.id
left join admin a on s.adminid=a.id
</select>
@Test
public void find2(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
List<Student> list=studentDao.findStudents();//调用接口中方法 对应的sql
System.out.println(list);
sqlSession.close();
}
2.2嵌套查询
使用嵌套查询,把sql分成多次查询,先查询学生信息,然后再查询宿舍、管理员
public interface StudentDao {
Student findStudentById1(int id);
}
<resultMap id="studentMap1" type="com.ffyc.mybatisPro.model.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="birthday" property="birthday"></result>
<result column="oper_time" property="operTime"></result>
<association property="dorm" javaType="com.ffyc.mybatisPro.model.Dorm" column="dormid" select="findDormById"></association>
<association property="admin" javaType="com.ffyc.mybatisPro.model.Admin" column="adminid" select="findAdminById"></association>
</resultMap>
<select id="findStudentById1" parameterType="int" resultMap="studentMap1">
select id,num,name,gender,birthday,dormid,adminid from student where id=#{id}
</select>
<select id="findDormById" resultType="com.ffyc.mybatisPro.model.Dorm">
select num from dorm where id=#{dormid} /*dormid是column="dormid"传过来的*/
</select>
<select id="findAdminById" resultType="com.ffyc.mybatisPro.model.Admin">
select account from admin where id=#{adminid}
</select>
@Test
public void find3(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
studentDao.findStudentById1(1);//调用接口中方法 对应的sql
sqlSession.close();
}
2.3 查询宿舍下所有的学生
宿舍和学生关联的话,会有多条记录,但是对于宿舍只有一个记录;将关联多个学生封装到一个集合对象中,在宿舍里面放一个学生的集合
public interface DormDao {
Dorm findDormById(int id);
}
<mapper namespace="com.ffyc.mybatisPro.dao.DormDao">
<resultMap id="dormMap" type="com.ffyc.mybatisPro.model.Dorm">
<id column="id" property="id"></id>
<result column="num" property="num"></result>
<association property="admin" javaType="com.ffyc.mybatisPro.model.Admin">
<result column="account" property="account"></result>
</association>
<!--用collection最终返回一个对象;ofType是:list类型中装的Student类型-->
<collection property="students" javaType="list" ofType="com.ffyc.mybatisPro.model.Student">
<!--result column="name"是Dorm中, property="name"是Student类中-->
<result column="snum" property="num"></result>
<result column="name" property="name"></result>
</collection>
</resultMap>
<select id="findDormById" parameterType="int" resultMap="dormMap">
select
d.id,
d.num,
s.num snum,
s.name,
a.account
from dorm d left join admin a on d.adminid=a.id
left join student s on d.id=s.dormid
where d.id=#{id}
</select>
</mapper>
@Test
public void find(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
DormDao dormDao = sqlSession.getMapper(DormDao.class);
Dorm dorm =dormDao.findDormById(1);//调用接口中方法 对应的sql
sqlSession.close();
}
注解方式
常用注解标签
@Insert:插入 sql , 和 xml insert sql 语法完全一样
@Select :查询 sql, 和 xml select sql 语法完全一样
@Update:更新 sql, 和 xml update sql 语法完全一样
@Delete :删除 sql, 和 xml delete sql 语法完全一样
@Param :入参
@Results:设置结果集合
@Result :结果
public interface StudentDao {
@Delete("delete from student where id=#{id}")
void deleteStudent(int id);
}
@Test
public void delete(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
studentDao.deleteStudent(1);//调用接口中方法 对应的sql
sqlSession.commit();
sqlSession.close();
}
@Update("update student set num = 103 where id = #{id}")
void updateState(int id);
@Select("select num,name from student where id = #{id}")
Student findStudent(int id);
Mybatis动态SQL
MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力。 如果你有使用JDBC 或其他相似框架的经验,你就明白条件地串联 SQL 字符串在一起是多么的痛苦,确保不能忘了空格或在列表的最后省略逗号。动态 SQL 可以彻底处理这种痛苦。
where、if
if test=“条件”:可以对传入的条件进行判断
对于查询条件个数不确定的情况,可使用元素。where标签:当where标签内的if有成立的,就会动态的添加一个where关键字;如果where后面有and、or这种关键字,也会动态删除
public interface StudentDao {
List<Student> findStudents1(Student student);
}
<select id="findStudents1" resultType="com.ffyc.mybatisPro.model.Student">
select * from student
<where>
<if test="num!=null">
and num=#{num}
</if>
<if test="name!=null">
and name=#{name}
</if>
<if test="gender!=null">
and gender=#{gender}
</if>
</where>
</select>
@Test
public void query(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student=new Student();
//student.setNum(103);
student.setName("李四");
studentDao.findStudents1(student);//调用接口中方法 对应的sql
sqlSession.close();
}
trim
trim中的prefix可以自定义指定关键字,如果if成立就动态的添加指定的关键字
prefixOverrides删除掉指定的开头的关键字
<select id="findStudents1" resultType="com.ffyc.mybatisPro.model.Student">
select * from student
<trim prefix="where" prefixOverrides="and|or">
<if test="num!=null">
and num=#{num}
</if>
<if test="name!=null">
and name=#{name}
</if>
<if test="gender!=null">
and gender=#{gender}
</if>
</trim>
</select>
choose、when、otherwise
相当于if…else if…else;choose里面可以有when,可以没有otherwise
<select id="findStudents1" resultType="com.ffyc.mybatisPro.model.Student">
select * from student
<trim prefix="where" prefixOverrides="and|or">
<choose>
<when test="num!=null">
num=#{num}
</when>
<when test="name!=null">
and name=#{name}
</when>
<otherwise>
and gender=#{gender}
</otherwise>
</choose>
</trim>
</select>
set
set 可以动态的添加set关键字,可以去除最后的逗号;主要用于多次修改中
public interface StudentDao {
void updateStudent(Student student);
}
<update id="updateStudent" parameterType="com.ffyc.mybatisPro.model.Student">
update student
<set>
<if test="num!=null">
num=#{num},
</if>
<if test="name!=null">
name=#{name},
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</set>
where id=#{id}
</update>
也可以用trim实现
<update id="updateStudent" parameterType="com.ffyc.mybatisPro.model.Student">
update student
<trim prefix="set" suffixOverrides=",">
<if test="num!=null">
num=#{num},
</if>
<if test="name!=null">
name=#{name},
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</trim>
where id=#{id}
</update>
@Test
public void update(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student=new Student();
student.setId(2);
student.setNum(104);
student.setName("李四44");
studentDao.updateStudent(student);//调用接口中方法 对应的sql
sqlSession.commit();
sqlSession.close();
}
foreach
对集合进行遍历(尤其是在构建 IN 条件语句的时候)
public interface StudentDao {
void deleteStudent(List<Integer> list);
}
<!--delete from student where id in (2,3)批量删除,对数组或集合进行遍历-->
<delete id="deleteStudent">
delete from student where id in
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
@Test
public void delete1(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
List<Integer> list=new ArrayList<>();
list.add(2);
list.add(3);
studentDao.deleteStudent(list);//调用接口中方法 对应的sql
sqlSession.commit();
sqlSession.close();
}
对数组进行遍历
public interface StudentDao {
void deleteStudent2(Integer[] array);
}
<delete id="deleteStudent2">
delete from student where id in
<foreach collection="array" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
@Test
public void delete2(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Integer[] array={4,5};
studentDao.deleteStudent2(array);//调用接口中方法 对应的sql
sqlSession.commit();
sqlSession.close();
}
标记语言中,特殊符号处理
方式一:转义符号处理。在 mybatis 中的 xml 文件中,存在一些特殊的符号,比如:<、>、"、&、<>等,正常书写 mybatis 会报错,需要对这些符号进行转义。具体转义如下所示:
特殊字符 | 转义字符 |
---|---|
< | < ; |
> | > ; |
" | " ; |
’ | &apos ; |
& | & ; |
public interface StudentDao {
List<Student> findStudents2(int num);
}
select id="findStudents2" resultType="com.ffyc.mybatisPro.model.Student">
select * from student where num<#{num}
</select>
@Test
public void query1(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
studentDao.findStudents2(103);//调用接口中方法 对应的sql
sqlSession.close();
}
方式二:除了可以使用上述转义字符外,还可以使用<![CDATA[]]>来包裹特殊字符。如下所示:
<select id="findStudents2" resultType="com.ffyc.mybatisPro.model.Student">
select * from student where num <![CDATA[ < ]]> #{num}
</select>
<![CDATA[ ]]>是 XML 语法。在 CDATA 内部的所有内容都会被解析器忽略。但是有个问题那就是 、 、 等这些标签都不会被解析,所以 我们只把有特殊字符的语句放在 <![CDATA[ ]]> ,**尽量缩小<![CDATA[ ]]> 的范围。**
缓存
缓存(cache)的作用是为了减去数据库的压力,提高查询性能。缓存实现的原理是从数据库中查询出来的对象在使用完后不要销毁,而是存储在内存(缓存)中,当再次需要获取该对象时,直接从内存(缓存)中直接获取,不再向数据库执行select 语句,从而减少了对数据库的查询次数,因此提高了数据库的性能。
一级缓存
生:创建sqlsession
销毁:close()、clearCache()、执行update,delete,insert
Mybatis 有一级缓存和二级缓存。一级缓存的作用域是同一个 SqlSession,在同一个 sqlSession 中两次执行相同的 sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据,不再从数据库查询,从而提高查询效率。当一个 sqlSession 结束后该 sqlSession 中的一级缓存也就不存在了。Mybatis 默认开启一级缓存。
@Test
public void cacheDemo1(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student s1=studentDao.findStudentById(6);//调用接口中方法 对应的sql
Student s2=studentDao.findStudentById(6);//调用接口中方法 对应的sql
sqlSession.close();
}
查询了两次,但是sql语句执行了一次?因为两次查询的SQL语句一样,查询id为6 返回的对象会缓存到SqlSession对象中,SqlSession对象在内存中存,如果下一次还查询id为6的,就直接访问SqlSession 从缓存中拿就可以
一级缓存的生命周期
a、MyBatis 在开启一个数据库会话时,会创建一个新的 SqlSession 对象,SqlSession 对象中会有一个新的 Executor 对象。Executor 对象中持有一个新的 PerpetualCache 对象;如果 SqlSession 调用了 close()方法,会释放掉一级缓存 PerpetualCache 对象,一级缓存将不可用。
@Test
public void cacheDemo1(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student s1=studentDao.findStudentById(6);//调用接口中方法 对应的sql
sqlSession.close();//清除缓存
SqlSession sqlSession2= MybatisUtil.getSqlSession();
StudentDao studentDao2 = sqlSession2.getMapper(StudentDao.class);
Student s2=studentDao2.findStudentById(6);//调用接口中方法 对应的sql
sqlSession.close();
}
b、如果 SqlSession 调用了clearCache(),会清空 PerpetualCache 对象中的数据,但是该对象仍可使用。
@Test
public void cacheDemo1(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student s1=studentDao.findStudentById(6);//调用接口中方法 对应的sql
sqlSession.clearCache();//清除一级缓存中的数据
Student s2=studentDao.findStudentById(6);//调用接口中方法 对应的sql
sqlSession.close();
}
c、SqlSession 中执行了任何一个 update 操作(update()、delete()、insert()) ,都会清空一级缓存的数据,但是该对象可以继续使用。
@Test
public void cacheDemo1(){
SqlSession sqlSession= MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student s1=studentDao.findStudentById(6);//调用接口中方法 对应的sql
Student student=new Student();
student.setId(6);
student.setNum(106);
student.setName("李四");
studentDao.updateStudent(student);//中间执行新增、删除、修改操作,一级缓存也会清除
Student s2=studentDao.findStudentById(6);//调用接口中方法 对应的sql
sqlSession.close();
}
二级缓存
二级缓存是 SqlSessionFactory 级别的,SqlSessionFactory只有一个。
SqlSession1创建,查询到的数据放在SqlSessionFactory中,SqlSession1销毁;SqlSession2创建,仍然可以拿到SqlSessionFactory中数据
配置二级缓存
第一步:启用二级缓存
在 mybatis_config.xml中启用二级缓存,如下代码所示,当cacheEnabled 设置为 true 时启用二级缓存,设置为 false 时禁用二级缓存。
<!--在mybatis_config.xml中配置-->
<settings> <!--全局开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
可以全局的开启缓存,也可以局部的开启或关闭缓存(useCache=“true”)
第二步:对象序列化
将所有的 model类实现序列化接口 Java.io. Serializable。
package com.ffyc.mybatisPro.model;
import java.io.Serializable;
public class Student implements Serializable {
//......
}
第三步:配置映射文件
在 Mapper 映射文件中添加,表示此 mapper 开启二级缓存。
当 SqlSeesion 关闭时,会将数据存入到二级缓存.
<!--StudentMapper.xml中配置二级缓存-->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"
/>
public class MybatisUtil {
static SqlSessionFactory sessionFactory;
//静态代码块,在类加载时执行一次
static {
Reader reader = null;
try {
reader = Resources.getResourceAsReader("mybatis_config.xml");
} catch (IOException e) {
e.printStackTrace();
}
sessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
//创建并返回SqlSession
public static SqlSession getSqlSession(){
return sessionFactory.openSession();
}
}
@Test
public void cacheDemo2(){
//用的是同一个SqlSessionFactory
SqlSession sqlSession= MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student s1=studentDao.findStudentById(6);//调用接口中方法 对应的sql
sqlSession.close();//一级缓存销毁,毁时会将缓存写入到二级缓存
SqlSession sqlSession2= MybatisUtil.getSqlSession();
StudentDao studentDao2 = sqlSession2.getMapper(StudentDao.class);
Student s2=studentDao2.findStudentById(6);
//此时可以从二级缓存获取数据,因为共享的是同一个SqlSessionFactory
sqlSession.close();
}
可以单独为某一个语句关闭二级缓存,加上useCache=“false”,即使配置过二级缓存,二级缓存也会失效
<select id="findStudentById" parameterType="int" resultMap="studentMap" useCache="false">
select
s.id,
s.num,
s.name,
s.gender,
s.birthday,
d.num dormNum,
a.account,
s.oper_time
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>