前言:
学习B站UP主狂神说[MyBatis]视频笔记整理
B站链接
MyBatis 是一款优秀的 持久层框架,它支持自定义 SQL、存储过程以及高级映射。
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到 Github。
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
GitHub: https://github.com/mybatis/mybatis-3
中文文档:https://mybatis.org/mybatis-3/zh/index.html
Dao层,service层,controller层
-- 创建数据库
CREATE DATABASE `mybatis`;
--如果存在则删除
DROP TABLE IF EXISTS `user`;
-- 创建数据库
CREATE TABLE `user`(
`id` int(20) NOT null PRIMARY KEY,
`name` varchar(30) DEFAULT null,
`pwd` VARCHAR(30) DEFAULT null
)ENGINE=INNODB DEFAULT charset=utf8;
-- 插入数据
insert into `user`(`id`,`name`,`pwd`) values (1,'狂神','123456'),(2,'张三','abcdef'),(3,'李四','987654');
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>cn.tonygroupId>
<artifactId>TestMybatisartifactId>
<packaging>pompackaging>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.17version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.12version>
<scope>providedscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-surefire-pluginartifactId>
<version>2.18.1version>
<configuration>
<skipTests>trueskipTests>
configuration>
plugin>
plugins>
build>
project>
参考官方文档,创建mybatis-config.xml
配置文件
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
mappers>
configuration>
<configuration>
<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?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
configuration>
从SqlSessionFactory
中获取到SqlSession
,就可以执行其中的SQL语句
SqlSession
相当于JDBC中的statement
package cn.tony.utils;
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.InputStream;
/**
* Mybatis工具类
* 获取SqlSessionFactory对象
* @author Tu_Yooo
* @Date 2021/3/9 10:40
*/
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory=null;
//从官方文档取出的代码 获取SqlSessionFactory
static{
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取SqlSession
*
* 既然有了 SqlSessionFactory,顾名思义,
* 我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
* 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
* @return
*/
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
/**
* User
* @author Tu_Yooo
* @Date 2021/3/9 10:56
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
/**
* Dao层接口
* @author Tu_Yooo
* @Date 2021/3/9 11:08
*/
public interface UserMapper {
public List<User> getUserList();
}
从官方文档可以看出,MyBatis几乎避免了几乎所有JDBC代码,
由原来的UserMapperimpl变成xml文件
所以这里我们不用编写接口实现类,直接创建UserMapper.xml
配置文件
<mapper namespace="cn.tony.dao.mapper.UserMapper">
<select id="getUserList" resultType="cn.tony.pojo.User">
select * from user
select>
mapper>
编写Junit测试文件 测试UserMapper
/**
* 测试UserMapper
* @author Tu_Yooo
* @Date 2021/3/9 11:31
*/
public class UserMapperTest {
@Test
public void testUser(){
//获取sqlSession
SqlSession sqlSession = MybatisUtil.getSqlSession();
//指向SQL
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.getUserList();
for (User user: list) {
System.out.println(user);
}
//关闭SqlSession
sqlSession.close();
}
}
1.通过编写的工具类,获取sqlSession
2.通过sqlSession
获取UserMapper
接口实例
3.执行方法获取结果
测试运行发现报错了!:
org.apache.ibatis.binding.BindingException: Type interface cn.tony.dao.mapper.UserMapper is not known to the MapperRegistry.
原因是核心配置文件中缺少指向UserMapper.xml的配置
继续运行发现还是报错了!
Error building SqlSession.
The error may exist in resources/UserMapper.xml
Caused by: java.io.IOException: Could not find resource resources/UserMapper.xml
这是Maven过滤的问题的原因 无法导出配置文件
解决:在pom.xml文件中加入以下配置
<build>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
resources>
build>
参考官方文档会发现调用SqlSession执行SQL有两种方式
官网推荐的是第二种,也就是我们刚刚使用的通过session.getMapper(BlogMapper.class);
方式一:
//获取sqlSession
SqlSession sqlSession = MybatisUtil.getSqlSession();
//指向SQL
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
方式二:
List<User> objects = sqlSession.selectList("cn.tony.dao.mapper.UserMaper.getList");
作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的 最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。
可以把它想象成数据库连接池
SqlSessionFactory 一旦被创建就应该在应用的 运行期间一直存在,没有任何理由 丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是 使用单例模式或者静态单例模式。
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例 不是线程安全的,因此是 不能被共享的,所以它的最佳的作用域是 请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}
在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。
1.在UserMapper
接口中新增方法
//指定id查询
public User getUser(int id);
2.在UserMapper.xml中新增SQL映射
<select id="getUser" resultType="cn.tony.pojo.User" parameterType="int">
select * from user where id=#{id}
select>
其中parameterType是入参类型
3.编辑测试类
@Test
public void testgetUser(){
//获取sqlSession
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUser(1);
System.out.println(user);
//关闭SqlSession
sqlSession.close();
}
1.在UserMapper
接口中新增方法
//新增用户
public int insertUser(User user);
2.在UserMapper.xml中新增SQL映射
<insert id="insertUser" parameterType="cn.tony.pojo.User" >
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
insert>
3.编辑测试类
@Test
public void TestinsertUser(){
//获取sqlSession
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.insertUser(new User(4, "王根基", "123456"));
System.out.println(i);
//关闭SqlSession
sqlSession.close();
}
运行发现成功了
但是实际数据库并没有新增一行数据
这是由于 增删改 必须要 提交事务
修改测试代码
//增删改必须要提交事务
@Test
public void TestinsertUser(){
//获取sqlSession
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.insertUser(new User(4, "王根基", "123456"));
System.out.println(i);
//提交事务
sqlSession.commit();
//关闭SqlSession
sqlSession.close();
}
1.在UserMapper
接口中新增方法
//修改用户
public int UpdateUser(User user);
2.在UserMapper.xml中新增SQL映射
<update id="UpdateUser" parameterType="cn.tony.pojo.User">
update user set name=#{name},pwd=#{pwd} where id=#{id}
update>
3.编辑测试类
@Test
public void updateUser(){
//获取sqlSession
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.UpdateUser(new User(1, "郑在稿", "123456"));
System.out.println(i);
//提交事务
sqlSession.commit();
//关闭SqlSession
sqlSession.close();
}
1.在UserMapper
接口中新增方法
// 删除用户
public int deleteUser(int id);
2.在UserMapper.xml中新增SQL映射
<delete id="deleteUser" parameterType="int">
delete from user where id=#{id} ;
delete>
3.编辑测试类
@Test
public void testdeleteUser(){
//获取sqlSession
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.deleteUser(2);
System.out.println(i);
//提交事务
sqlSession.commit();
//关闭SqlSession
sqlSession.close();
}
每次都要手动提交事务,相对麻烦
查看SqlSessionFactory源码可知,其中openSession(),可以设置是否自动提交事务
修改MybatisUtil工具类
在实际工作中 如果实体类或数据库字段过多,使用对象User
的入参方式,过于繁琐且每个字段必须得赋值
<update id="UpdateUser" parameterType="cn.tony.pojo.User">
update user set name=#{name},pwd=#{pwd} where id=#{id}
update>
此时我们可以使用Map来做参数的传递:
1.在UserMapper
接口中新增map修改方法
//修改用户map
public int updateMapUser(Map<String,Object> map);
2.在UserMapper.xml中新增SQL映射
<update id="updateMapUser" parameterType="map">
update user set name=#{nameMap} where id=#{idMap}
update>
#{}内的名字可以任意定义,只需要与map中的K保持一致即可
3.编辑测试类
@Test
public void testupdateMapUser(){
//获取sqlSession
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("nameMap","郑爽");
map.put("idMap",2);
int i = mapper.updateMapUser(map);
System.out.println(i);
//提交事务
sqlSession.commit();
//关闭SqlSession
sqlSession.close();
}
map传递参数,直接在SQL中取Key即可
对象传递参数,直接取对象的属性即可
如果只有单个基本类型参数,parameterType
可以不写
分页查询的作用在于减少数据处理量
编写UserMapper接口
//分页查询
public List<User> limitgetUser(Map<String,Integer> map);
在UserMapper.xml中新增SQL映射
<select id="limitgetUser" parameterType="map" resultType="user">
select * from user limit #{stateInt},#{pageInt}
select>
编写测试类
@Test
public void testlimitgetUser(){
//获取sqlSession
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("stateInt",0);
map.put("pageInt",2);
List<User> list = mapper.limitgetUser(map);
for (User user : list) {
logger.info("查询结果:"+user);
}
//关闭SqlSession
sqlSession.close();
}
阅读官方文档:
对于一些简单的SQL,我们完全可以不用xml配置文件的方式来定义.可以使用注解来进行配置
编写接口
//使用注解开发
@Select("select * from user")
public List<User> getUserannation();
编写测试类
@Test
public void Testannotion(){
//获取sqlSession
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserannation();
for (User user : userList) {
logger.info("查询结果:"+user);
}
//关闭SqlSession
sqlSession.close();
}
编写接口
//传入多个参数 使用@Param注解
@Select("select * from where id = #{id}")
public User getUserannotionid(@Param("id") int id);
@Param注解用于给方法参数起一个名字。以下是总结的使用原则:
编写接口
@Insert("insert into user (id,name,pwd) values (#{userid},#{name},#{password})")
public int insertUser2(User user);
mybatis-config.xml
参考官方文档,需要重点掌握的是以下这几个属性
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456
<configuration>
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="UserMapper.xml"/>
mappers>
configuration>
也可以在properties标签增加一些其他的属性
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="pwd" value="123456"/>
properties>
如果标签与配置文件都指定了同一个字段,优先使用properties配置文件内的
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等
常见使用以下配置
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
sttings标签在properties标签和typeAliases中间 位置固定
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
扫描完包,默认会使用 Bean 的首字母小写的非限定类名来作为它的别名
<typeAliases>
<typeAlias alias="user" type="cn.tony.pojo.User"/>
typeAliases>
<typeAliases>
<package name="cn.tony.pojo"/>
typeAliases>
实体类比较少的时候,建议使用第一种方式
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
<configuration>
<environments default="test">
<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?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="UserMapper.xml"/>
mappers>
configuration>
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
官网提示:
配置数据源来连接数据库
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):
UNPOOLED– 这个数据源的实现会 每次请求时打开和关闭连接。虽然有点慢, 但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。
POOLED– 这种数据源的 实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。默认使用
JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。
映射器是告诉 MyBatis 到哪里去找映射文件
相对于类路径的资源引用 这种方式是官网推荐的
使用class绑定注册
注意点:
ResultMap是解决实体类与数据库字段名不一致,导致查询出来的结果无法正确赋值的问题
resultMap 元素是 MyBatis 中最重要最强大的元素
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int userid;
private String name;
private String password;
}
如果查询数据,那么实体类与数据库字段名不一致的字段必然无法正确赋值
这个时候就需要使用resultMap来定义结果集映射
<resultMap id="usermap" type="user">
<result column="id" property="userid"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
resultMap>
<select id="getUserList" resultMap="usermap">
select * from user
select>
如果此时你已经对resultMap 足够了解,那么有些实体类与数据库名字相同的字段,完全可以不用显示的定义出来
<resultMap id="usermap" type="user">
<result column="id" property="userid"/>
<result column="pwd" property="password"/>
resultMap>
如果这个世界总是这么简单就好了。
MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。 如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,但可惜也没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。
新建数据库表
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师');
CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');
创建数据库实体类
学生类
/**
* 学生类
* @author Tu_Yooo
* @Date 2021/3/12 16:00
*/
@Data
public class Student {
private int id;
private String name;
//学生关联一个老师
private Teacher teacher;
}
老师类
/**
* 老师类
* @author Tu_Yooo
* @Date 2021/3/12 16:02
*/
@Data
public class Teacher {
private int id;
private String name;
}
创建接口
/**
* 学生类
* @author Tu_Yooo
* @Date 2021/3/12 16:05
*/
public interface StudentMapper {
}
/**
* 老师类接口
* @author Tu_Yooo
* @Date 2021/3/12 16:05
*/
public interface TeacherMapper {
}
创建Mapper.xml配置文件
<mapper namespace="cn.tony.dao.mapper.TeacherMapper">
mapper>
<mapper namespace="cn.tony.dao.mapper.StudentMapper">
mapper>
核心配置文件中添加映射
<mappers>
<mapper resource="UserMapper.xml"/>
<mapper resource="StudentMapper.xml"/>
<mapper resource="TeacherMapper.xml"/>
mappers>
需求:查询所有学生 以及对应的老师信息
/**
* 学生类
* @author Tu_Yooo
* @Date 2021/3/12 16:05
*/
public interface StudentMapper {
//查询所有学生 以及对应的老师信息
public List<Student> selectStudent();
}
<mapper namespace="cn.tony.dao.mapper.StudentMapper">
<resultMap id="studentmap" type="student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="cn.tony.pojo.Teacher" select="selectTeacher"/>
resultMap>
<select id="selectStudent" resultMap="studentmap">
select * from student
select>
<select id="selectTeacher" resultType="teacher">
select * from teacher where id =#{id}
select>
mapper>
<mapper namespace="cn.tony.dao.mapper.StudentMapper">
<resultMap id="studentmap" type="student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" javaType="cn.tony.pojo.Teacher" >
<result property="id" column="tid"/>
<result property="name" column="tname"/>
association>
resultMap>
<select id="selectStudent" resultMap="studentmap">
select a.*,b.id as tid,b.name as tname from student a
inner join teacher b on a.tid=b.id
select>
mapper>
修改实体类
/**
* 老师类
* @author Tu_Yooo
* @Date 2021/3/12 16:02
*/
@Data
public class Teacher {
private int id;
private String name;
private List<Student> student;
}
/**
* 学生类
* @author Tu_Yooo
* @Date 2021/3/12 16:00
*/
@Data
public class Student {
private int id;
private String name;
//学生关联一个老师
private int pid;
}
需求:获取指定老师下的所有学生及老师的信息
/**
* 老师类接口
* @author Tu_Yooo
* @Date 2021/3/12 16:05
*/
public interface TeacherMapper {
//查询指定老师下的所有学生信息及老师信息
public Teacher selectTeacher(@Param("tid") int id);
}
?selectTeacherxml version="1.0" encoding="UTF-8" ?>
<mapper namespace="cn.tony.dao.mapper.TeacherMapper">
<resultMap id="getTeacher" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="stud" ofType="Student" >
<result property="id" column="sid"/>
<result property="name" column="sname"/>
collection>
resultMap>
<select id="selectTeacher" resultMap="getTeacher">
select a.id as tid,a.name as tname,b.id as sid,b.name as sname from teacher a
inner join student b on a.id=b.tid
where a.id = #{tid}
select>
mapper>
1.如果实体类字段为对象选择-association
2.如果实体类字段为集合 使用-collection
3. javaType
是指定Java类属性
4. ofType
获取指定集合中POJO泛型信息
动态SQL就是指根据不同的条件生成不同的SQL
创建数据库表
CREATE TABLE `blog` (
`id` varchar(50) NOT NULL COMMENT '博客id',
`title` varchar(100) NOT NULL COMMENT '博客标题',
`author` varchar(30) NOT NULL COMMENT '博客作者',
`create_time` datetime NOT NULL COMMENT '创建时间',
`views` int(30) NOT NULL COMMENT '浏览量'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
创建POJO实体类
/**
* 博客表
* @author Tu_Yooo
* @Date 2021/3/13 15:42
*/
@Data
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}
编写博客接口表
/**
* 博客接口类
* @author Tu_Yooo
* @Date 2021/3/13 15:47
*/
public interface BlogMapper {
}
编写博客xml映射文件
<mapper namespace="cn.tony.mapper.BlogMapper">
mapper>
在核心配置文件中引入
<mappers>
<mapper resource="BlogMapper.xml"/>
mappers>
编写UUID工具类
/**
* 生成id工具类
* @author Tu_Yooo
* @Date 2021/3/13 15:53
*/
public class IDutils {
public static String getId(){
return UUID.randomUUID().toString().replaceAll("-","");
}
}
在核心配置文件中设置开启驼峰命名规则
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
插入数据
编写接口
//插入数据
public int insertBlog(Blog blog);
编写xml
<mapper namespace="cn.tony.mapper.BlogMapper">
<insert id="insertBlog" parameterType="blog">
insert into blog (id,title,author,create_time,views)
values (#{id},#{title},#{author},#{createTime},#{views});
insert>
mapper>
插入数据
//测试插入数据
@Test
public void testaddBlog(){
SqlSession session = MybatisUtil.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IDutils.getId());
blog.setTitle("Mybatis如此简单");
blog.setAuthor("狂神说");
blog.setCreateTime(new Date());
blog.setViews(9999);
mapper.insertBlog(blog);
blog.setId(IDutils.getId());
blog.setTitle("Java如此简单");
mapper.insertBlog(blog);
blog.setId(IDutils.getId());
blog.setTitle("Spring如此简单");
mapper.insertBlog(blog);
blog.setId(IDutils.getId());
blog.setTitle("微服务如此简单");
mapper.insertBlog(blog);
session.close();
}
编写接口
//查询博客
public List<Blog> selectBlog(Map map);
编写XML文件
<select id="selectBlog" parameterType="map" resultType="blog">
select * from blog where 1=1
/*如果传入author或title则匹配后面的值*/
<if test="author != null">and author=#{author} if>
<if test="title != null"> and title=#{title}if>
select>
编写测试类
@Test
public void testSelectBlog(){
SqlSession session = MybatisUtil.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
Map<String, String> map = new HashMap();
map.put("title","Java如此简单");
List<Blog> list = mapper.selectBlog(map);
for (Blog blog : list) {
System.out.println(blog);
}
session.close();
}
在实际生产环境,SQL不能用where 1=1
这样不安全的查询
可以使用where
标签匹配后面的查询
修改XML文件
<select id="selectBlog" parameterType="map" resultType="blog">
select * from blog
<where>
<if test="author != null">author=#{author} if>
<if test="title != null"> and title=#{title}if>
where>
select>
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
trim>
用于 动态更新语句 的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。
有些时候我们可能将公共(重复) 的SQL抽取出来,方便复用
这就是
标签
使用与注意点:
抽取公共的部分
标签修改XML文件
<sql id="if-author-title">
<if test="author != null">author=#{author} if>
<if test="title != null"> and title=#{title}if>
sql>
<select id="selectBlog" parameterType="map" resultType="blog">
select * from blog
<where>
<include refid="if-author-title"/>
where>
select>
choose像Java 中的 switch 语句。
只会匹配其中一个值
修改XML文件
<select id="selectBlog" parameterType="map" resultType="blog">
select * from blog
<where>
<choose>
<when test="title != null"> title = #{title}when>
<when test="author != null">and author =#{author}when>
<otherwise> and views =#{views}otherwise>
choose>
where>
select>
阅读官方文档:
入参类型list集合 index集合中的下标 item集合中的元素
select * from POST p where id in ([开始符] 1 ,[分隔符]2,[分隔符],3[分隔符] )[结束符]
需求:查询id为1,2,3的用户
编辑接口
//查询用户id为1,2,3的用户
public List<Blog> getBlog(Map map);
编辑XML文件
<select id="getBlog" parameterType="map" resultType="blog">
select * from blog
<where>
id in
<foreach collection="ids" item="userId" index="index" open="(" separator="," close=")">
#{userId}
foreach>
where>
select>
demo测试
//查询用户id为1,2,3的用户
@Test
public void test2(){
SqlSession session = MybatisUtil.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
Map<String, List> map = new HashMap();
//准备list集合
ArrayList<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
map.put("ids",ids);
List<Blog> blog = mapper.getBlog(map);
for (Blog blog1 : blog) {
System.out.println(blog1);
}
session.close();
}
在数据库的使用过程中,很多时候我们需要借助日志来进行排错
在以往我们使用:sout,debug
现在Mybatis提供了日志工厂
SLF4J
LOG4J (掌握)
LOG4J2
JDK_LOGGING
COMMONS_LOGGING
STDOUT_LOGGING (掌握)
NO_LOGGING
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
值得注意的是 sttings标签在properties标签和typeAliases中间 位置固定,否则会报错
配置完日志,在查询时就能有以下日志输出了,有助于我们日常排错
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等
我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
**
* 测试log4j
* @author Tu_Yooo
* @Date 2021/3/10 16:48
*/
public class Log4jTest {
//注意导包:org.apache.log4j.Logger
static Logger logger = Logger.getLogger(Log4jTest.class);
@Test
public void testlog4j(){
logger.info("普通级别输出日志");
logger.debug("debug级别输出日志");
logger.error("紧急级别输出日志");
}
}
1.什么是缓存[ Cache ]?
存在内存中的临时数据
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了 高并发系统的性能问题。
2.为什么使用缓存?
减少和数据库的交互次数,减少系统开销,提高系统效率。
3.什么样的数据能使用缓存?
经常查询并且不经常改变的数据。
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
一级缓存也叫本地缓存:sqlSession
测试步骤:
1.开启日志
2.测试在一个sqlSession会话中是否查询两次数据库
编辑接口
public User getUser(@Param("id") int id);
编辑XML文件
<select id="getUser" resultType="cn.tony.pojo.User">
select * from user where id=#{id}
select>
测试并查看日志
@Test
public void testgetUser(){
//获取SQLsession
SqlSession sqlSession = MybatisUtil.getSqlSession();
//获取接口实现类
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUser(1);
System.out.println(user);
System.out.println(">>>>>>>>>>>>>>>>");
User user2 = mapper.getUser(1);
//判断两个对象是否是一个
System.out.println(user2);
System.out.println(user2==user);
sqlSession.close();
}
1.查询不同的数据
2.增删改完要刷新缓存
3.手动清除缓存sqlSession.clearCache();//手动清楚缓存
4.查询不同的Mapper.xml
一级缓存只在sqlSession会话中有效,也就是拿到连接到关闭连接这个区间段
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
工作机制
缓存是默认关闭的需要手动开启二级缓存
二级缓存生效机制:一级缓存sqlSession1号去世的时候会将数据放进二级缓存中,sqlSession2号使用的时候会去二级缓存中取
步骤:
1.开启全局缓存
在mybatis-config.xml
中设置
<settings>
<setting name="cacheEnabled" value="true"/>
settings>
2.在要使用二级缓存的Mapper.xml
映射文件中设置
也可以自定义参数
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
3.在Mapper.xml
映射文件中设置可以为不需要使用缓存的方法单独设定
<select id="getUser" resultType="cn.tony.pojo.User" useCache="false">
select * from user where id=#{id}
select>
注意点:
我们需要将实体类序列化,否则会报错
总结:
1.只要开启了二级缓存,在同一个Mapper下就有效
2.所有的数据都会先放进一级缓存中
3.只有当会话提交,或者关闭的时候,才会提交到二级缓存
1.先看二级缓存有没有数据
2.再看一级缓存中有没有数据
3.查询数据库
Ehcache是一种广泛使用的 java分布式缓存,用于通用缓存;
要在应用程序中使用Ehcache,需要引入依赖的jar包
<dependency>
<groupId>org.mybatis.cachesgroupId>
<artifactId>mybatis-ehcacheartifactId>
<version>1.1.0version>
dependency>
在mapper.xml
中使用对应的缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
创建ehcahe.xml
配置文件,里面提供一些自定义配置
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="./tmpdir/Tmp_EhCache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
ehcache>