什么是mybatis?
- 它支持定制化 sql 、储存过程以及高级映射,是一个持久层框架
- 几乎避免了所有的 JDBC 代码和手动设置参数以及结果集
- 使用简单的注解或者xml 来映衬原生类型、接口和java 的POJO
持久层
数据持久化
- 持久化就是将程序的数据在持久状态和瞬时状态转换的过程
内存:断电及失
温馨提示:后面学习,记得
sqlSession.close ()
和sqlSession.commit ()
哦 ~~
。
基础
官网:
https://mybatis.net.cn/
使用方法步骤如下所示:
①在 resource 中核心配置文件 mybatis-config.xml
url配置 = jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8&useSSL=false
<?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="${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>
②创建一个 Utils 类,获取sqlSessionFactory对象
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
③既然有了sqlSessionFactory的实例,顾名思义,我们也就可以获得 sqlSession的实例了。sqlSession 完全包含了面向数据库执行sql命令所需的所有的方法。
。
以下将案例中整体的代码展示如下图(重点用红色标记):
解决找不到resource下的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>
纠结好久的问题
Mybatis中,返回一个多条数据(List)时,resultType的值指定的不是List,而是List中的元素类型
CRUD
namespace
:要和对应的 Dao/mapper 接口一致
select 中的 id
:就是对应的namespace中使用方法的方法名必须要保持一致
resultType
:就是要返回的参数类型
案例如下:
模糊查询 Map 万能(不正规的野路子)
假设,我们的实体类,或者数据库中的表,或者字段参数过多,我们应当考虑使用Map
代码演示如下(UserMapper、UserMapper.xml、mybatisTest):
void addUser2(Map<String,Object> key);
<insert id="addUser2" parameterType="map" >
insert into mybatisTest (id,name,pwd) values (#{uid},#{uname},#{upwd})
</insert>
public void addUser2(){
SqlSession sqlSession = mybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Map<String,Object> map = new HashMap<String,Object>();
map.put("uid","8");
map.put("uname","xjj");
map.put("upwd","123");
usersMapper.addUser2(map);
sqlSession.commit();
sqlSession.close();
}
模糊查询
以上方法存在 sql 注入
推荐:
配置之属性优化
示例:
或者:
.
.
都可以。
别名 ⇒ typeAlises
方法一(实体类比较少建议):
方法二(实体类比较多的情况):
可以使用Bean的首字母小写的非限定类名来作为别名。
如果有注解,可以使用注解起别名。
Mappers
resource注册(推荐使用)
每一个Mapper都需要来注册
<Mappers>
<Mapper resource="xxx.xml"/>
<Mappers>
class注册
每一个接口都必须来注册,并且,Mapper和配置文件必须同名,同文件下才可
<Mappers>
<Mapper class = " xxxMapper "/>
<Mappers>
name扫描包
Mapper和配置文件必须同名,同文件下才可
<Mappers>
<package name = "xxx"/>
</Mappers>
resultMap ⇒ 结果集映射
日志
log4j
①配置maven
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
②创建log4j.properites
百度查找 log4j 配置文件,很详细
### 设置Logger输出级别和输出目的地(控制台和文件夹) ###
log4j.rootLogger=debug, stdout,logfile
### 把日志信息输出到控制台 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
###控制台输出的格式为.err红色字体###
log4j.appender.stdout.Target=System.err
###控制台输出的布局方式###
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
### 把日志信息输出到文件:jbit.log ###
log4j.appender.logfile=org.apache.log4j.FileAppender
### 设置输出文件夹的名字 ###
log4j.appender.logfile.File=jbit.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
### 自定义输出日志的格式 ###
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}%l %F %p %m%n
#日志级别从高到低分为:
#A:off 最高等级,用于关闭所有日志记录。
#B:fatal 指出每个严重的错误事件将会导致应用程序的退出。
#C:error 指出虽然发生错误事件,但仍然不影响系统的继续运行。
#D:warn 表明会出现潜在的错误情形。
#E:info 一般和在粗粒度级别上,强调应用程序的运行全程。
#F:debug 一般用于细粒度级别上,对调试应用程序非常有帮助。
#G:all 最低等级,用于打开所有日志记录。
#Log4j提供的appender有以下几种:
#org.apache.log4j.ConsoleAppender(控制台),
#org.apache.log4j.FileAppender(文件),
#org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),
#org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),
#org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
# %d 输出日志时间点的日期或时间
# %l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。
# %F: 输出日志消息产生时所在的文件名称
# %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
# %m: 输出代码中指定的消息,产生的日志具体信息
# %n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”
③配置log4j为日志实现
④使用log4j
分页
方法一:
方法二:
面相接口编程
官方案例:
个人测试:
- 一般比较简单的可以这么写,但是这么写有局限性。比较复杂的话还是最好使用 XML 来配置。
- 注解使用了反射机制。
注解的CRUD ⇒ 细节补充
我们可以在工具类创建的时候,自动创建事务。(俗称自动 sqlSession.commit() )
如果方法存在多个参数的时候,所有参数前面必须加上 @Param()。俗称,绑定参数。
参数中,#{id} (建议使用,不存在sql注入) 和 ${id} (不推荐,存在sql注入)
Lombok ⇒ (偷懒必备神器)⇒ 不推荐使用,但是灵活使用哦 ~~ 。
我们不用导入一些包了,可以用 Lombok 自动配置。
安装方法建议百度,那边更详细一点点。
- 在 Idea 安装插件
- 在项目中导入Jar包
- 使用@Getter、@Setter 、@Log4j 等常用的包/操作
案例: @Data 会自动生成 get、set、toString、hashcode、equals 等方法。
复杂环境搭建
对于学生这边而言,多个学生,关联一个老师【多对一】
对于老师这边而言,一个老师,关联多个学生【一对多】
多对一
遇到的问题:
这种情况下,怎么办呢 ?
① 按照查询嵌套处理(有问题,已经处理,在本小结的后面)
问题处理
在 id 为getTeacher 的 mysql 语句中,后面的
where id=#{id}
,中,id到底是哪一个呢?这个参数id是怎么传递下来的呢?解释如下图所示:
向导①:同学们注意看,在查询这个 “mybatis02” 这个sql中,我们是有三个列的,分别是
id,name,teacher(int类型),我们再看②号向导映射的 Student 实体类 这张图,也有三个属性,id,name,teacher(Student属性)。
而 column 映射的是 向导① 中mysql中的 teacher(int类型的值),而引向的是下面的select,所以我们就可以直接用 mysql语句中的teacher值啦!
唠叨一句,请分清楚,什么是主表,什么是主映射哦 ~~
② 按照结果查询处理(已处理)
.
Pojo:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
private Teacher teacher;
}
Interface:
public interface Students {
// 多对一结果集映射
List<Student> getStudent();
}
xml映射文件:
<!-- 多对一结果集映射 -->
<resultMap id="getTeacher" type="Test.Pojo.Student">
<result column="sid" property="id" />
<result column="sname" property="name" />
<association property="teacher" javaType="Test.Pojo.Teacher">
<result column="tname" property="name"></result>
<result column="tid" property="id"></result>
</association>
</resultMap>
test:
@Test
public void test(){
SqlSession sqlSession = new MybatisUtils().getSession();
Students students = sqlSession.getMapper(Students.class);
List<Student> stu = students.getStudent();
for(Student s:stu){
System.out.println(s.getId()+","+s.getName()+"-->"+s.getTeacher().getName());
}
sqlSession.close();
}
遇到的问题
如图所示:
明明映射完 Teacher 直接用就可以,为什么还要 result 一个属性呢?
.
结论(个人观点):因为我们如果不写,就只告诉java我们需要映射Teacher类的一个模子 和 mysql 得出的值,并没有说将 mysql 的哪一个得出的值,映射到哪一个模子中的值里面去。所以,我们需要搭一下线。
一对多
结果集映射案例:
javaType 用来指定对象所属的 java 数据类型,也就是 private List <Post> posts 的 ArrayList 类型
ofType 用来指定对象的所属 javaBean 类,也就是尖括号的泛型 private Listposts
明白话:javatype 和 oftype 都是指代对象类型的,但是 javaType 是用来指待 Pojo 中的属性类型,而 ofType 指定的是映射到 list 集合属性中的 pojo 类型。
实战:
子查询
动态SQL
什么是动态SQL?动态SQL就是根据不同的条件生成不同的SQL。
实例:
动态 IF 语句
例子已经摆出来啦~~
我们可以看到第三张图,这边是 IF 的使用方式咯 ~~ 同学可以研究一下下~~
遇到的问题(我可是解决了老半天嘞,快让我装个x)
先看错误示例:
我在教程上看到的是这个样子的代码,但是怎么测试,都报错,图如下
这个问题其实就是接收不到 name 参数,我们可以手动引导一下。
还有就是 new Hashmap 在创建的时候,传递 “name” 属性,有可能会乱码,具体解决请百度哟 ~~。
choose / where / otherwise ⇒ 必须至少传递这个值(限制条件)
choose
set
trim:可以去百度了解一下,还是很实用的!
摘抄狂神笔录:所谓的动态sql,本质上还是sql语句,只是我们可以在sql的层面上去执行一个逻辑代码
forEach
可以百度一下,不经常使用。一般还是 where 等比较多。
缓存
重点: 二级缓存的使用条件 ⇒ 一级缓存结束 或者 提交(转存)(同一个mapper下有效)
查询:需要链接数据库 ⇒ 比较消耗资源
一次性查询的结果,给他暂时存放到一个可以直接取到的地方! ⇒ 内存:缓存
我们再次查询数据的时候,直接走缓存,就不用去走数据了。(可以解决高并发系统的性能问题)
使用二级缓存
开启缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中。如果当前会话关闭了,这个会话对应的一级缓存就没了。但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中,新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中;
使用缓存
使用测试展示:
自定义(Ehcache)⇒ 用的人逐渐变少,可以去百度了解,先不用深究。
此处略,本人没学习,各位需要的小伙伴自己去百度吧。