Mybatis
环境
- jdk1.8
- Mysql 5.7
- maven 3.5.4
- IDEA 2019.2
储备知识
- JDBC
- Mysql
- Java基础
- Maven
- Junit
简介
什么是Mybatis
- MyBatis 是一款优秀的持久层框架
- 它支持自定义SQL、存储过程以及高级映射
- MyBatis 免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作
//设置参数
...
pst.setInt(1,4);
pst.setString(2,"DJ");
...
//获取结果集
...
ResultSet rs = statement.executeQuery(sql);
...
- MyBatis 可以通过简单的 XML或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
xml就是可扩展标记语言,在Java中一般是作为配置文件。
- MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis
- 2013年11月迁移到Github。
如何获得Mybatis?
- 方法一:从github获取 链接
持久化
数据持久化
- 数据持久化就是将数据从内存中的数据模型转化为存储模型
- 内存:断电即失
- 比如:数据库(jdbc)io支持持久化
- 生活例子:冷藏、罐头
为什么需要持久化?
- 有一些对象,不能让他丢掉
- 内存太贵了
持久层
- 完成持久化工作的代码块
- 层界限十分明显
为什么需要Mybatis
- 方便
- 传统的JDBC代码太复杂了,简化,框架
- 将sql和代码解耦(sql文件就是放在xml,而之前的JDBC则是将两者合在一起)
- 容易上手
第一个Mybatis程序
搭建环境-->导入Mybatis-->编写代码-->测试
步骤
-
在数据库创建一个
my_school
,执行sql文件 -
创建一个普通的maven项目
- 删除src目录
- 导入依赖
- 需要在idea中下载lombok插件
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
- 创建一个模块
- 子模块中存在
<parent>
<artifactId>mybatis-01</artifactId>
<groupId>com.jmu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
- 父模块中存在
<modules>
<module>mybatis-1</module>
</modules>
- 编写mybatis的核心配置文件
mybatis-config.xml
- 注意点:用
&
表示&
- 注意点:用
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 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/my_school?serverTimezone=GMT&useUnicode=true&characterEncoding=utf8&useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>
-
编写mybatis的工具类
SqlSessionFactoryBuilder 一旦创建了 SqlSessionFactory,就不再需要它了 局部变量 SqlSessionFactory SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在 应用作用域,比如:使用单例模式或者静态单例模式。 SqlSession 每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它(sqlSession.close()) 请求或方法作用域,用完关闭 SqlSession openSession(boolean var1);
设置成true,就可以实现自动提交事务了,就不需要在增删改中手动写commit
package com.jmu.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;
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
//从 XML 中构建 SqlSessionFactory
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//从 SqlSessionFactory 中获取 SqlSession
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
- 编写代码
- 实体类Stu
- 和数据库中的字段一一对应
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Stu {
private String sno;
private String sname;
private int age;
private boolean sex;
private int mno;
}
- DAO接口
package com.jmu.dao;
import com.jmu.pojo.Stu;
import java.util.List;
public interface StuDao {
List<Stu> getAllStu();
Stu getStuByName(String name);
}
- 实现DAO接口
由原来的Impl文件转化为mapper.xml文件
渐渐的将××DAO.java改成××Mapper.java
<?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.jmu.dao.StuDao">
<select id="getAllStu" resultType="com.jmu.pojo.Stu">
select * from stu where sname like #{input_name}
</select>
<select id="getStuByName" resultType="com.jmu.pojo.Stu">
select * from stu
</select>
</mapper>
- 测试
在test/java/com/jmu/dao/StuTest
,在这个地方是为了规范
package com.jmu.dao;
import com.jmu.pojo.Stu;
import com.jmu.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class StuTest {
@Test
public void getUserList(){
//第一步 获得sqlSession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
//第二步 执行SQL getMapper
StuDao stuMapper=sqlSession.getMapper(StuDao.class);
System.out.println(stuMapper.getStuByName("小一"));
List<Stu> all = stuMapper.getAllStu();
for(Stu one:all){
System.out.println(one);
}
//第三步 关闭sqlSession
sqlSession.close();
}
}
- 测试结果
注意的是:你的测试结果可能会出现报错,继续向下看
Stu(sno=2020001, sname=小一, age=18, sex=false, mno=1)
Stu(sno=2020001, sname=小一, age=18, sex=false, mno=1)
Stu(sno=20200010, sname=小十, age=20, sex=false, mno=3)
Stu(sno=20200011, sname=小快, age=19, sex=false, mno=3)
Stu(sno=20200012, sname=小冬, age=21, sex=false, mno=3)
Stu(sno=20200013, sname=小宇, age=19, sex=false, mno=0)
Stu(sno=20200014, sname=小点, age=19, sex=false, mno=4)
Stu(sno=20200015, sname=彭杰, age=21, sex=false, mno=4)
Stu(sno=20200016, sname=彭小杰, age=21, sex=false, mno=4)
Stu(sno=2020002, sname=小二, age=18, sex=true, mno=1)
Stu(sno=2020003, sname=小四, age=18, sex=true, mno=1)
Stu(sno=2020004, sname=小五, age=18, sex=true, mno=1)
Stu(sno=2020005, sname=小六, age=18, sex=false, mno=2)
Stu(sno=2020006, sname=小七, age=18, sex=true, mno=2)
Stu(sno=2020007, sname=小八, age=18, sex=false, mno=2)
Stu(sno=2020008, sname=小九, age=18, sex=true, mno=2)
Stu(sno=2020009, sname=小十, age=19, sex=false, mno=3)
碰到的问题
报错一
Type interface com.jmu.dao.UserDao is not known to the MapperRegistry.
每一个***Mapper.xml
都需要到mybatis-config.xml
中注册。加到configuration中
<mappers>
<mapper resource="com/jmu/dao/StuMapper.xml"/>
</mappers>
报错二
Could not find resource com/jmu/dao/UserMapper.xml
Maven的资源过滤问题
需要在pom.xml加入以下代码(在父项目添加即可,如果还不行的话,就在子module的pom.xml也添加)
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
报错三
1 字节的 UTF-8 序列的字节 1 无效。
把xml的encoding属性值UTF-8改为UTF8==,Mapper.xml文件和mybatis-config.xml都需要改动
CRUD
namespace
namespace中的包名和Dao/Mapper接口的包名一致
select
- id:就是对应的namespace的方法名
- resultType:sql语句执行的返回值
- parameterType:参数
{}与${}的区别
MyBatis中使用parameterType向SQL语句传参,parameterType后的类型可以是基本类型int,String,HashMap和java自定义类型。
#{}
#
将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。
例如:order by #{parameterName}
假设传入参数是"Smith"
会解析成:order by "Smith"
${}
$
将传入的数据直接显示生成在sql中。
例如:order by #{parameterName}
假设传入参数是"Smith"
会解析成:order by Smith
实践总结:
#
方式能够很大程度防止sql注入,$方式无法防止Sql注入。从安全性上考虑,能使用#
尽量使用#
来传参,因为这样可以有效防止SQL注入的问题。- 当传入的是字段名或者表名的时候使用
${}
模糊查询
- 在java代码的时候,传递通配符
%张%
,
List<User> userList = mapper.getUserLike("%李%");
- 在sql拼接中使用
like '%${value}%'
select * from user where name like "%"#{value}"%"
增删改需要提交事务 sqlSession.commit();
insert
<insert id="insertUser" parameterType="com.jmu.pojo.User" >
insert into user(username, password, age) values(#{username},#{password},#{age})
</insert>
@Test
public void insert(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserDao userMapper = sqlSession.getMapper(UserDao.class);
int n=userMapper.insertUser(new User("admin","123",12));
System.out.println(n);
sqlSession.commit();
sqlSession.close();
}
update
和insert基本一样
万能的Map
假设,我们的实体类,或者数据库中的表,字段或者参数过多。或者联表查询结果
插入数据
<insert id="insertUser2" parameterType="map" >
insert into user(username, password, age) values(#{name},#{pwd},#{age})
</insert>
@Test
public void insert(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserDao userMapper = sqlSession.getMapper(UserDao.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("name","zhangsan");
map.put("pwd","mima");
map.put("age",18);
int n=userMapper.insertUser2(map);
System.out.println(n);
sqlSession.commit();
sqlSession.close();
}
联表查询
<select id="getUserInfo" resultType="map" parameterType="int">
select * from user,address where user.auto_id=address.user_id and address.user_id=#{user_id}
</select>
@Test
public void get(){
SqlSession sqlSession=MybatisUtil.getSqlSession();
UserDao userMapper = sqlSession.getMapper(UserDao.class);
Map<String,Object> map=userMapper.getUserInfo(1);
//遍历key value
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
}
传递类型 | 说明 | 例子 |
---|---|---|
Map传递参数 | 直接在 sql中取出Key即可 | parameter="map" |
对象传递参数 | 直接在sql中取出对象的属性即可 | parameter="com.jmu.pojo.××" |
只有一个基本类型参数 | 可以直接在sql中取到 | parameter="int“ 也可以不写 |
多个参数传递 | 使用Map或者注解 |
配置解析
核心配置文件
configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- MyBatis 可以配置成适应多种环境,(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
环境配置(environments)
MyBatis 可以配置成适应多种环境,不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单:
- 每个数据库对应一个 SqlSessionFactory 实例
Mybatis有2种事务管理器(JDBC|MANAGED)默认的事务管理器是JDBC
默认数据源类型是POOLED(池)
属性(properties)
我们可以通过properties属性来引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
编写好db.properties
注意这里的&不使用&
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/demo?serverTimezone=GMT&useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456
报错:mybatis核心配置文件,需要注意标签配置顺序
The content of element type "configuration" must match "(properties?,settin....
- 可以直接引入配置文件
<properties resource="db.properties" />
- 也可以在内部引入属性配置
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
- 但是外部引入的优先级大于内部引入
类型别名(typeAliases)
- 设置一个缩写名字
- 意在降低冗余的全限定类名书写。例如:
<typeAliases>
<typeAlias type="com.jmu.pojo.User" alias="User" />
</typeAliases>
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="com.jmu.pojo"/>
</typeAliases>
每一个在包 com.jmu.pojo
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 com.jmu.pojo.User
的别名为 user
;
- 在实体类比较少的时候,可以使用第一种
- 在实体类比较多的时候,可以使用第二种
- 第一种可以 diy别名,第二种如果非要改别名,可以加注解
@Alias("author")
public class Author {
...
}
设置(settings)
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 |
true | false | false |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
映射器(mappers)
方式一【推荐使用】:使用相对于类路径的资源引用
注意是斜杆,不是点
<mappers>
<mapper resource="com/jmu/dao/UserMapper.xml"></mapper>
</mappers>
方式二:使用class文件绑定注册
- 注意点
- 接口和Mapper配置文件必须同名
- 接口和Mapper配置文件必须在同一个包下
<mappers>
<mapper class="com.jmu.dao.UserMapper" />
</mappers>
解决属性名和字段不一致的问题
解决方法:
- 起别名(as)
数据库字段 as 属性名
select password as pwd ,username as name,age from user where auto_id=#{auto_id}
- resultMap(注意我们去掉看resultType)
- 结果映射
- column 数据库列 property 属性名
<resultMap id="UserMap" type="User">
<result column="username" property="name"/>
<result column="password" property="pwd"/>
</resultMap>
<select id="getUserList" resultMap="UserMap">
select * from user
</select>
日志
日志工厂
如果一个数据库操作,出现异常,我们需要排错。日志是最好的助手!
曾经:sout debug
现在:日志工厂
logImpl 【掌握】LOG4J |STDOUT_LOGGING(标准日志输出)
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
Log4J
什么是Log4J
- Log4J是Apache的一个开源项目,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
- 我们也可以控制每一条日志的输出格式
- 通过定义每一条日志信息的级别,我们能更加细致地控制日志的生成过程
- 通过配置文件来灵活进行配置,不需要修改代码
直接使用
- 先导入log4j的包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 在resources中新建
log4j.properties
#将等级为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/jmu.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
- 配置log4j为日志实现
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
- 直接测试运行
输出到文件
- 在要使用Log4J的类中,导入
import org.apache.log4j.Logger;
- 注意:包不要导错了
- 日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(UserTest.class);
- 日志级别
logger.info("info:进入testLog4j方法");
logger.debug("debug:进入testLog4j方法");
logger.error("error:进入testLog4j方法");
分页
思考:为什么要分页?
- 减少数据的处理量
使用limit分页
select * from user limit startIndex,pageSize;
使用Mybatis实现分页,核心SQL
- 接口
//分页
List<User> getUserByLimit(Map<String,Integer> map);
- Mapper.xml
<select id="getUserByLimit" parameterType="map" resultType="User">
select * from demo.user limit #{startIndex},#{pageSize}
</select>
- 测试
@Test
public void getUserByLimit(){
Map<String,Integer> map = new HashMap<String, Integer>();
map.put("startIndex",1);
map.put("pageSize",2);
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> all = userMapper.getUserByLimit(map);
for (User user : all) {
System.out.println(user);
}
}
使用注解开发
面向接口编程
我们之前学过面向对象编程,但是真正的开发中,很多时候,我们会选择面向接口编程
根本原因:解耦
使用注解开发
映射的语句可以不用 XML 来配置,而可以使用 Java 注解来配置。比如
package org.mybatis.example;
import org.apache.ibatis.annotations.Select;
public interface BlogMapper {
@Select("SELECT * FROM blog WHERE id = #{id}")
Blog selectBlog(int id);
}
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
步骤
- 注解在接口上实现
- 注意:方法存在有多个参数,所有参数一定要加@Param,取的参数主要是根据@Param里面的
@Select("select * from user where auto_id = #{auto_id}")
User getUserById(@Param("auto_id") int id);
- 需要在核心配置文件中绑定接口
<mappers>
<mapper class="com.jmu.dao.UserMapper" />
</mappers>
关于@param()注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型,可以忽略,但是建议都加上
- 我们在SQL中引用的就是我们这里的@Param()设定的属性名
复杂查询
数据库准备
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');
多对一处理
多个学生对应一个老师
测试环境搭建
- 新建实体类 Teacher Student
private int id;
private String name;
//学生需要关联一个老师
private Teacher teacher;
- 建立Mapper接口
- 建立Mapper.xml文件
可以在resources中新建com.jmu.dao中放Student.xml和TeacherMapper.xml
这样处理,在最后生成的时候会变成
- 在核心配置文件中添加
<mappers>
<mapper resource="com/jmu/dao/StudentMapper.xml" />
<mapper resource="com/jmu/dao/TeacherMapper.xml" />
</mappers>
5.测试查询是否成功
按照查询嵌套处理
- 思路
- 先查询出所有的学生
- 根据学生的tid查询出对应的老师
- 复杂属性我们需要单独处理
- 对象 association
- 集合 collection
<mapper namespace="com.jmu.dao.StudentMapper">
<resultMap id="StudentTeacher" type="Student">
<result column="id" property="id"></result>
<result column="name" property="name"></result>
<association column="tid" property="teacher" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id = #{tid}
</select>
</mapper>
按照结果嵌套处理
<resultMap id="StudentTeacher2" type="Student">
<result column="sid" property="id"></result>
<result column="sname" property="name"></result>
<association property="teacher" javaType="Teacher" >
<result column="tname" property="name" />
</association>
</resultMap>
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid,
s.name sname,
t.name tname
from student s,teacher t
where s.tid=t.id;
</select>
总结:Mysql多对一
- 联表查询
- 子查询
一对多处理
一个老师拥有多个学生
测试环境搭建
- 编写实体类
public class Student {
private int id;
private String name;
private int tid;
....
}
public class Teacher {
private int id;
private String name;
//一个老师教多个学生
private List<Student> students;
....
}
按结果 嵌套处理
- javaType用来指定实体类中的属性的类型
- ofType 用来指定映射到List或者集合中pojo类型
<resultMap id="TeacherStudent" type="Teacher">
<result column="tid" property="id" />
<result column="tname" property="name" />
<collection property="students" ofType="Student">
<result column="sid" property="id"></result>
<result column="sname" property="name"></result>
</collection>
</resultMap>
<select id="getTeacher" resultMap="TeacherStudent">
select t.id tid,
t.name tname,
s.id sid,
s.name sname
from student s,teacher t
where s.tid=t.id;
</select>
面试高频
- Mysql引擎
- InnoDB底层原理
- 索引
- 索引优化
动态SQL
什么是动态SQL:动态SQL就是根据不通过的条件生成不同的SQL语句
if
choose
trim
foreach
数据库准备
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
项目搭建
public class Blog {
private int id;
private String title;
private String author;
private Date create_time;
private int views;
...
}
if
test中的title
和"%"#{title}"%"
是后端传过来的map的String,不是数据库的。即
map.put("title1","标题");
<when test="title1 != null">
title like "%"#{title1}"%"
</when>
<select id="getBlogIf" parameterType="map" resultType="Blog">
select * from blog where 1=1
<if test="title != null">
and title like "%"#{title}"%"
</if>
<if test="views != null">
and views = #{views}
</if>
</select>
choose、when、otherwise
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。switch-case
<select id="getBlogIf" parameterType="map" resultType="Blog">
select * from blog
<where>
<choose>
<when test="title != null">
title like "%"#{title}"%"
</when>
<when test="author != null and views != null">
author like #{author} and views = #{views}
</when>
<otherwise>
views >100
</otherwise>
</choose>
</where>
</select>
trim、where、set
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
select * from blog
<where>
<if test="title != null">
and title like "%"#{title}"%"
</if>
<if test="views != null">
and views = #{views}
</if>
</where>
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
foreach
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句
的时候)。比如:
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
缓存(了解 )
- 什么是缓存?
- 存在内存中的临时数据
- 将用户经常查询的数据放在缓存(内存)中,用户去查询就不用从磁盘上(关系数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决高并发系统的性能问题
- 为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率
- 什么样的数据能够使用缓存?
- 经常并且不经常改变的数据
Mybatis缓存
Mybatis系统默认定义两级缓存:一级缓存和二级缓存
-
默认情况下,只有一级缓存开启(SqlSession级别的缓存,也称为本地缓存)
-
二级缓存需要手动开启和配置,他是基于namespace级别的缓存
-
为了提高扩展性,Mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存
一级缓存
又称本地缓存:SqlSession
- 与数据库同一次会话期间查询的数据会放在本地缓存中
- 以后如果相同的数据 ,直接从缓存中拿,没必要再去查询 数据库
测试步骤
- 开启日志
- 测试在一个SqlSession中查询两次相同的记录
@Test
public void test(){
SqlSession sqlSession = MybatisUtils getsqlSession();
UserMapper mapper =sqlSession getMapper(UserMapper class);
User user = mapper.queryUserById(1);
System.out.println(user):
System.out.println("======================");
User user2 = mapper.queryUserById(1);
System.out.println(user2):
System.out.println("======================");
System. out. println(user==user2);//返回true,说明两个对象地址引用是相同的
sqlSession.close();
}
//观察日志发现只是走了一次sql
二级缓存
- 二级缓存也叫全局缓存 ,一级缓存的作用域太低,所以诞生二级缓存
要启用全局的二级缓存,只需要在你的 SQL 映射文件xml中添加一行<cache/>
:
需要开启全局缓存(在setting中)
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
<mapper namespace="com.jmu.dao.BlogMapper">
<cache/>
......
测试
开启两个sqlSession,一个执行完之后,将sqlSession关闭,另一个再去查询,看到SQL执行一次
小结:
- 只要开启了二级缓存,在同一个Mapper下就有效
- 所有的数据都会先放在一级缓存中
- 只有当Sqlseesion会话提交,或者关闭的时候,才会提交到二级缓存中
- 用户查询数据,先看二级缓存有没有,再看一级缓存,最后看数据库
自定义缓存
除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。