Mybatis
这里写目录标题
1、简介
1.1、简介
1.简介
- MyBatis 是一款优秀的持久层框架
- 它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
2.如何获得Mybatis
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
1.2、持久化
数据持久化
- 持久化就是将程序的数据在持久状态和顺势状态转化的过程
- 内存:断电即失
- 数据库、IO文件持久化
为什么要持久化?
- 有一些对象是不能丢失的
- 内存太贵
1.3、持久层
概念:完成持久化工作的代码块,层界限十分明显
2、第一个mybatis程序
思路:搭建环境–>导入Mybatis–>编写代码–>测试
2.1、搭建环境
1.搭建数据库环境:创建数据库 数据表
CREATE DATABASE `Mybatis`;
USE `Mybatis`;
CREATE TABLE `user`(
`id` INT(11) PRIMARY KEY,
`name` VARCHAR(20) NOT NULL,
`password` VARCHAR(20) DEFAULT NULL
)ENGINE = INNODB DEFAULT CHARSET = utf8;
INSERT INTO `user` (`id`,`name`,`password`) VALUES
(1,"zhangsan","123"),
(2,"李四","2312312"),
(3,"王五","23124")
2.新建一个普通的maven项目,删除src目录 这样就可以把这个maven当成一个父工程 ,并在父工程导入依赖
<dependencies>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!-- Mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
2.2、创建一个子模块
好处:不用每次新建一个模块的重新导依赖了
1.编写Mybatis核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<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?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 每一个Mapper.xml都需要在核心配置文件中注册-->
<mappers>
<mapper resource="com/morant/mybatis/Mapper/UserMapper.xml"/>
</mappers>
</configuration>
2.编写Mybatis工具类
//sqlSessionFactory-->sqlsession
public class MybatisUtils {
public static SqlSessionFactory sqlSessionFactory;
static{
try {
//使用Mybatis第一步:获取sqlSessionFactory对象
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();
}
}
2.3、编写代码
- 实体类
package com.morant.mybatis.entity;
public class User {
private int id;
private String name;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
- Mapper接口
public interface UserMapper {
public List<User> find();
}
- 实现接口配置文件
<?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.morant.mybatis.Mapper.UserMapper">
<select id="find" resultType="com.morant.mybatis.entity.User">
select * from user
</select>
</mapper>
注意:常见错误
1、org.apache.ibatis.binding.BindingException: Type interface com.Mybatis.Mapper.UserMapper is not known to the MapperRegistry(错误原因:未注册Mapper.xml,每一个Mapper.xml都需要在核心配置文件中注册)
解决办法:
<!-- 在核心配置文件Mybatis-config.xml中配置-->
<mappers>
<mapper resource="com/Mybatis/Mapper/UserMapper.xml"/>
</mappers>
2、Caused by: java.io.IOException: Could not find resource com/morant/Mapper/UserMapper.xml(错误原因:Maven本身的原因,我们写的配置文件无法被导出)
<!--在build中配置resources,来防止我们资源导出失败的问题--><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>
2.4 测试
public class UserMapperTest { @Test public void a(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.find(); for (User user : users) { System.out.println(user); } sqlSession.close(); }}
3、CURD(增删改查需要提交事务)
1、select
- id:对应的namespace中的方法名;
- resultType:sql语句执行的返回值 Class、基本类型
- parameterType:参数类型
- 案例:查找一个用户
<select id="findid" resultType="com.morant.mybatis.entity.User" parameterType="int"> select * from user where id=#{id};</select>
2、insert
-
id:对应的namespace中的方法名;
-
parameterType:参数类型
-
案例:插入一个用户
<insert id="insert" parameterType="com.morant.mybatis.entity.User"> <!--传入参数类型设置的是用户的实体类 因此可以直接写属性名字 作为参数--> insert into user (id,name,password) values (#{id},#{name},#{password})</insert>
注意:测试的时候需要提交事务 sqlSession.commit();
3、update
-
id:对应的namespace中的方法名;
-
parameterType:参数类型
-
案例:修改一个用户
<update id="update" parameterType="com.morant.mybatis.entity.User"> update user set name=#{name},password=#{password} where id=#{id}</update>
注意:测试的时候需要提交事务 sqlSession.commit();
4、delete
-
id:对应的namespace中的方法名;
-
parameterType:参数类型
-
案例:删除一个用户
<delete id="delete" parameterType="int"> delete from user where id =#{id}</delete>
注意:测试的时候需要提交事务 sqlSession.commit();
5、万能的Map
假设,我们实体类或者数据库表中,字段或参数过多,我们应当考虑使用map传参,此时parameterType=“map”
- Map传递参数,直接在sql中取出key即可!parameterType=“map”
- 对象传递参数,直接在sql中取出对象的属性即可!parameterType=“Object”
- 只有几个基本数据类型的情况下,可以直接在sql中取到!parameterType=“int”可以不写
多个参数用Map或者注解
4、配置解析
1、配置环境(environment)
MyBatis可以配置适应多种环境
不过要记住:尽管可以配置多个环境,但是每个SqlSessionFactory实例只能选择一个环境
MyBatis默认的事务管理器就是JDBC,连接池:POOLED
2、属性(properties)
我们可以通过properties属性来实现引用配置文件
这些属性都是可外部配置且可动态替换的,即可以在典型的java属性文件[db.properties]中配置,亦可以通过properties元素的子元素来传递
1.编写一个db.properties配置文件
driver = com.mysql.jdbc.Driver
url:jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
user:root
password:123456
2.在核心配置文件中引入
核心配置文件xml中规定的标签顺序
<!-- 引入配置文件 --><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>
- 可以直接引入外部文件
- 可以再其中增加一些属性
- 如果两个文件有同一个字段,优先使用外部配置文件的
3、设置(setting)
4、类型别名(typeAliases)
- 类型名是为Java类型设置一个短的名字
- 存在的意义仅在于用来减少类完全限定名的冗余
1、给类起别名
<typeAliases> <typeAlias type="com.morant.mybatis.entity.User" alias="User"></typeAlias></typeAliases>
2、扫描实体类的包,他默认别名就位这个包内所在类的类名,首写字母小写!(如果实体类上面有@Alias("")注解 则必须写注解名)
<typeAliases> <package name="com.morant.mybatis.entity"/></typeAliases>
在实体类比较少的时候 建议使用第一种
实体类比较多的时候 推荐使用第二种
5、其他配置
6、映射器(Mapper)
MapperRegistry:注册绑定我们的mapper文件
方式一【推荐使用】
<mappers> <mapper resource="com/morant/mybatis/Mapper/UserMapper.xml"/></mappers>
方式二:使用class的文件绑定注册
<mappers> <class class="com/morant/mybatis/Mapper/UserMapper"/></mappers>
注意点:
- 接口和他的配置文件必须同名
- 接口和他的配置文件必须在同一个包下
方式三:使用扫描包进行绑定
<mappers> <package name="com/morant/mybatis/Mapper"/></mappers>
注意点:
- 接口和他的配置文件必须同名
- 接口和他的配置文件必须在同一个包下
7、生命周期和作用域
5、ResultMap
1、发现问题
实体类属性:
private int id;
private String name;
private String secret;
数据库对应字段
实体类和数据库对应字段不一致的时候 执行select方法之后 secret为null
解决方法:
- 起别名
<select id="find" resultType="user">
select id,name,password as secret from user
</select>
- 采用结果集映射
<resultMap id="UserMapper" type="com.morant.mybatis.entity.User">
<!-- 其中colum代表数据库中的字段 property代表实体类中的属性值-->
<result column="id" property="id" />
<result column="name" property="name" />
<result column="password" property="secret" />
</resultMap>
<select id="find" resultMap="UserMapper">
select * from user
</select>
2、结果集映射 ResultMap
数据库字段:id name password实体类属性:id name secret
<resultMap id="UserMapper" type="com.morant.mybatis.entity.User"> <!-- 其中colum代表数据库中的字段 property代表实体类中的属性值--> <result column="id" property="id" /> <result column="name" property="name" /> <result column="password" property="secret" /></resultMap><select id="find" resultMap="UserMapper"> select * from user</select>
- resultMap元素是Mybatis中最重要最强大的元素
- ResultMap的设计思想是:对于简单的语句根本不需要配置显示的结果映射,而对于复杂一点的语句只需要描述他们的关系就行了
- ResultMap最优秀的地方在于虽然你已经对他相当了解了,但是根本就不需要显示的用到他们
6、日志
6.1、日志工厂(STDOUT_LOGGING )
如果一个数据库的sql语句操作,出现了异常,我们需要排错,做好的帮手就是日志!
工厂日志的设置
name:
logImpl
value:
- SLF4J
- LOG4J 【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING 【掌握】
- NO_LOGGING
在Mybatis中具体使用哪一个日志实现,在设置中设定,
STDOUT_LOGGING标准日志的配置
在核心配置文件mybatis-config.xml中配置日志工厂
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/></settings>
日志信息
6.2、LOG4J
概述:
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
- 我们也可以控制每一条日志的输出格式;
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
- 最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
1、先导包
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version></dependency>
2、创建log4j.properties配置文件
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码log4j.rootLogger=DEBUG,console,file#控制台输出的相关设置log4j.appender.console = org.apache.log4j.ConsoleAppenderlog4j.appender.console.Target = System.outlog4j.appender.console.Threshold=DEBUGlog4j.appender.console.layout = org.apache.log4j.PatternLayoutlog4j.appender.console.layout.ConversionPattern=[%c]-%m%n#文件输出的相关设置log4j.appender.file = org.apache.log4j.RollingFileAppenderlog4j.appender.file.File=./log/morant.loglog4j.appender.file.MaxFileSize=10mblog4j.appender.file.Threshold=DEBUGlog4j.appender.file.layout=org.apache.log4j.PatternLayoutlog4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n#日志输出级别log4j.logger.org.mybatis=DEBUGlog4j.logger.java.sql=DEBUGlog4j.logger.java.sql.Statement=DEBUGlog4j.logger.java.sql.ResultSet=DEBUGlog4j.logger.java.sql.PreparedStatement=DEBUG
3、在核心配置文件mybatis-config.xml中将log4j配置为日志的实现
<settings> <setting name="logImpl" value="LOG4J"/></settings>
4.Log4j的使用!直接运行
5、简单的使用
- 要在Log4j的类中,导入包import org.apache.log4j.Logger;
- 日志对象 参数为当前类的class
static Logger logger = Logger.getLogger(UserMapperTest.class);
- 日志级别
logger.info("info:进入了testLog4j")logger.debug("debug:进入了testLog4j")logger.error("error:进入了testLog4j")
7、分页
1、使用Limit分页
语法:SELECT * from user limit startIndex,pageSize;SELECT * from user limit 3;#[0,3] 只写一个参数 从零开始到写的数字
使用Mybatis实现分页,核心Sql
1.接口
//分页查询 public List<User> limit (Map<String,Integer> map);
2.Mapper.xml
<select id="limit" parameterType="map" resultMap="UserMapper">
select * from user limit #{startIndex},#{pageSize}
</select>
3.测试
@Test
public void b(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> limit = mapper.limit(map);
for (User user : limit) {
System.out.println(user);
}
sqlSession.close();
}
2、RowBounds分页
8、使用注解开发
本质:反射机制实现
底层:动态代理!
入门:
1.注解在接口上实现
public interface UserMapper { @Select("select * from user") public List<User> find();}
2.需要在核心配置文件中绑定接口
<mappers> <mapper class="com.morant.mybatis.Mapper.UserMapper" /></mappers>
3.测试
@Testpublic void a(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.find(); for (User user : users) { System.out.println(user); } sqlSession.close();}
CURD
我们可以在工具类中设置自动提交事务
public class MybatisUtils { public static SqlSessionFactory sqlSessionFactory; static{ try { //使用Mybatis第一步:获取sqlSessionFactory对象 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(true); }}
8.1、编写注解 增加接口@selete
@selete
UserMapper(@Param中的变量名就是sql对查找的参数)
//根据id查询用户@Select("select * from user where id=#{id} and name=#{nname}")public User findid(@Param("id") int id,@Param("nname")String name);
测试
@Testpublic void b(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User findid = mapper.findid(2,"李四"); System.out.println(findid); sqlSession.close();}
8.2、编写注解 增加接口@Insert
@Insert
//添加用户信息@Insert("insert into user (id,name,password) values (#{id},#{name},#{secret})")public int insert(User user);
测试
@Testpublic void c(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.insert(new User(4,"第四个","123")); sqlSession.close();}
8.3、编写注解 增加接口@Update
@Update
//修改用户信息@Update("update user set name=#{name},password=#{password} where id =#{id}")public int update(@Param("id") int id,@Param("password")String password,@Param("name")String name);
测试
@Testpublic void d(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int one = mapper.update(1, "1", "one"); sqlSession.close();}
8.4、编写注解 增加接口@Delete
Delete
//删除用户
@Delete("delete from user where id = #{id}")
public int delete(@Param("id") int id);
测试
@Test
public void e(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int delete = mapper.delete(4);
sqlSession.close();
}
8.5、关于Param()注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话可以忽略,但是建议加上
- 我们SQL中引用的就是Param()中设定的属性名
9、Lombok
使用步骤:
1.在IDEA中安装Lombok插件!
2.在项目中导入Lombok的jar包
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>provided</scope></dependency>
3.在实体类上加注解即可!
@Getter and @Setter@FieldNameConstants@ToString@EqualsAndHashCode@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog@Data@Builder@SuperBuilder@Singular@Delegate@Value@Accessors@Wither@With@SneakyThrows
@Data:无参构造、get、set、toString、hashcod、equals
10、多对一处理
-
多个学生,对应一个老师
-
对于学生而言,关联…多个学生,关联一个老师【多对一】
-
对于老师而言,集合…一个老师有很多学生【一对多】
1、环境搭建
- 创建新的数据表Teacher、Student
#创建老师表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`);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GkwSTdqa-1629956787711)(C:\Users\mohai\AppData\Roaming\Typora\typora-user-images\image-20210824222359394.png)]
- 导入Lombok的jar包
<dependencies> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency></dependencies>
- 新建Teacher、Student实体类
@Data@AllArgsConstructor@NoArgsConstructorpublic class Student { private int id; private String name; private int tid;}
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Teacher {
private int id;
private String name;
}
- 新建TeacherMapper、StudentMapper接口
public interface StudentMapper {
}
public interface TeacherMapper {
//通过ID查找老师
@Select("select * from teacher where id = #{tid}")
public Teacher findbyId(@Param("tid") int id);
}
- 新建TeacherMapper.xml、StudentMapper.xml实现接口
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.morant.mybatis.Mapper.StudentMapper">
</mapper>
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.morant.mybatis.Mapper.TeacherMapper"></mapper>
- 在核心配置文件中注册接口
<mappers> <mapper class="com.morant.mybatis.Mapper.TeacherMapper" /> <mapper class="com.morant.mybatis.Mapper.StudentMapper" /></mappers>
- 测试
public class TeacherMapperTest { @Test public void a(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class); Teacher teacher = mapper.findbyId(1); System.out.println(teacher); sqlSession.close(); }}
2、多对一处理
方法一(类似子查询的方法),按照查询嵌套处理,写两个select语句
- StudentMapper
//方法一:(子查询)查询所有学生public List<Student> findStudent();
- StudentMapper.xml
<!-- 思路: 1.查询所有学生信息 2.根据查询出来的学生的tid,查找相应的老师--><select id="findStudent" resultMap="StudentTeacher"> select * from student</select><resultMap id="StudentTeacher" type="com.morant.mybatis.entity.Student"> <result property="id" column="id"/> <result property="name" column="name"/> <!-- 复杂的属性 我们需要单独处理,对象用association标签 集合用collection标签 --> <association property="teacher" column="tid" javaType="com.morant.mybatis.entity.Teacher" select="findTeacher"/></resultMap><select id="findTeacher" resultType="com.morant.mybatis.entity.Teacher"> select * from teacher where id = #{tid}</select>
方法二(嵌套处理的思路),按照结果嵌套处理,一条查询语句
- StudentMapper
//方法二:(连表查询)查询所有学生public List<Student> findStudent2();
- StudentMapper.xml
<!--思路二:按照嵌套处理 --><select id="findStudent2" resultMap="StudentTeacher2"> select s.id sid,s.name sname,s.tid tid,t.name tname from student s,teacher t where s.tid = t.id</select><resultMap id="StudentTeacher2" type="com.morant.mybatis.entity.Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" javaType="com.morant.mybatis.entity.Teacher"> <!--相当于在这里嵌套查询 对Teacher属性做说明--> <result property="name" column="tname"/> <result property="id" column="tid"/> </association></resultMap>
11、一对多处理
1、环境搭建(省略)
2、一对多查询
方法一(类似子查询的方法),按照查询嵌套处理,写两个select语句
- StudentMapper
//子查询
public Teacher findTStudent2(@Param("tid")int id);
- StudentMapper.xml
<!-- 子查询 -->
<select id="findTStudent2" resultMap="TStudent2">
select * from teacher where id = #{tid}
</select>
<resultMap id="TStudent2" type="com.morant.mybatis.entity.Teacher">
<!--
1.JavaType 用来指定实体类中属性的类型
2.ofType 用来指定映射到List或者集合中pojo类型,泛型中的约束类型
-->
<collection property="students" column="id" javaType="ArrayList" ofType="com.morant.mybatis.entity.Student" select="find"/>
</resultMap>
<select id="find" resultType="com.morant.mybatis.entity.Student">
select * from student where tid=#{tid}
</select>
方法二(嵌套处理的思路),按照结果嵌套处理,一条查询语句
- StudentMapper
//嵌套查询 public Teacher findTStudent(@Param("tid")int id);
- StudentMapper.xml
<!-- 嵌套查询 -->
<select id="findTStudent" resultMap="TStudent">
SELECT s.id sid,s.name sname,t.id tid,t.name tname
from student s,teacher t
where t.id=#{tid} and s.tid = t.id
</select>
<resultMap id="TStudent" type="com.morant.mybatis.entity.Teacher">
<result column="tid" property="id"/>
<result column="tname" property="name"/>
<!--
2.ofType 用来指定映射到List或者集合中pojo类型,泛型中的约束类型
-->
<collection property="students" ofType="com.morant.mybatis.entity.Student">
<result property="id" column="sid"/>
<result property="tid" column="tid"/>
<result property="name" column="sname"/>
</collection>
</resultMap>
3、小结:
1.关联 -association【多对一】
2.集合 -collection 【一对多】
3.javaType & ofType
1.JavaType 用来指定实体类中属性的类型
2.ofType 用来指定映射到List或者集合中pojo类型,泛型中的约束类型
注意点:
- 保证SQL的可读性,尽量保证通俗易懂
- 注意一对多和多对一中,属性名和字段的问题!
12、动态SQL
什么是动态SQL:动态SQL就是值根据不同的条件生成不同的SQL语句
1、IF标签
标签:如过if内的语句成立就追加
<select id="selectBlog" parameterType="map" resultType="com.morant.mybatis.entity.Blog">
select * from blog where 1=1
<if test="author != null">
and author = #{author}
</if>
<if test="views != null">
and views = #{views}
</if>
</select>
2、where
标签:where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
select * from blog title = #{title} and author = #{author} and views = #{views}3、choose、when、otherwise
标签:类似java中的switch、case、default
<select id="selectBlog" parameterType="map" resultType="com.morant.mybatis.entity.Blog">
select * from blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
title = #{title}
</when>
<otherwise>
views = #{views}
</otherwise>
</choose>
</where>
</select>
4、set
标签: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>
5、SQL片段
有时候,我们可能会将一些功能的片段部分抽取出来,方便复用
1.使用SQL标签抽取公共部分
<sql id="if-author-views" >
<if test="author != null">
and author = #{author}
</if>
<if test="views != null">
and views = #{views}
</if>
</sql>
2.在需要引用的地方使用Include标签引用即可
<include refid="id-author-views"></include>
注意事项:
- 最好基于单标定义Sql
- 不要存在where标签
13、缓存
13.1:一级缓存
1.概念
- 一级缓存也叫作本地缓存:Sqlsession
- 与数据库同一次会话期间查询的的数据会放在本地缓存
- 以后如果需要获取相同的数据,直接从缓存中拿,就没记要再去查询数据库
2.测试步骤:
1.开启日志
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
2.测试一次sqlSession
public void a(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.findById(1);
System.out.println(user1);
System.out.println("===========================");
User user2 = mapper.findById(1);
System.out.println(user2);
System.out.println(user1 == user2);
sqlSession.close();
}
3.缓存失效的情况
- 查询不同的东西
- 增删改查操作,改变原来的数据,所有缓存会刷新
- 查询不同的Mapper.xml
- 手动清理缓存
sqlSession.clearCache();
4.小结:
一级缓存默认是开启的,只在一次SqlSession中有效,也就是从拿到连接到关闭连接这个区间!一级缓存就是一个Map
13.2:二级缓存
1.概念
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
- 工作机制
- 一个会话查询一条语句,这个数据就会被放在当前会话的一级缓存中
- 如果当前缓存关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中
- 新的会话查询信息,就可以从二会话中获取内容
- 不同的mapper查出的数据会放在自己对应的缓存(map)中
2.测试步骤
1.开区全局缓存 并开启日志
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
2.在要使用二级缓存的Mapper中开启
<cache/>
也可以自定义一些参数
3.测试
未序列化报错
解决办法 实现Serializable接口
public class User implements Serializable {
private int id;
private String name;
private String password;
}
4.小结
- 只要开启了二级缓存,在同一个Mapper下就有效
- 所有的数据都会先放在一级缓存中
- 只有当会话提交,或者关闭的时候,才会提交到二级缓存中
13.3:缓存原理
缓存顺序:
1.先看Mapper二级缓存有没有
2.再看一级SqlSession缓存中有没有
本文根据狂神说Mybatis教学制作笔记,教学视频地址
https://www.bilibili.com/video/BV1NE411Q7Nx?.