目录
一.概述
1.什么是Mybatis?
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
简化理解:
MyBatis是一个半自动ORM的持久层框架,其本质是对JDBC的封装。使用MyBatis重点需要程序员编写SQL命令,不需要写一行JDBC代码。
!!!疑问:
a.ORM
含义: 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。
简化理解:ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。
作用:ORM框架会根据映射完成对数据库的操作,就不需要再去和复杂的SQL语句打交道了。
/*ORM是不需要写SQL语句就可以完成相关操作,相当于自动,Mybatis是需要写SQL语句,但是不如JDBC那么复杂,所以叫做半自动ORM*/
b.持久层框架
含义:即专注于实现数据持久化应用领域的某个特定系统的一个逻辑层面,将数据使用者和数据实体相关联。
简化理解:将内存中的数据永久存入对应数据库中(主要),也可以是其他文件中。(少部分,例如:磁盘文件,普通的text文件等)。
/*内存太过于昂贵,并且断电后会失去数据,所以需要持久化保存的话,需要将内存中的数据移到外存中,才能更好的保存数据,并且节约成本*/
2.mybatis的基本使用(最基础的用法)
a.建立数据库
b.导入Mybatis相关的jar包
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
c.编写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="${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>
其中mapper的引用有种方式:
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
environments中的内容也可以进行优化。
第一步.创建一个properties文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/数据库名?useSSL=true&useUnicode=true&characterEncoding
=utf8
username=用户名
password=密码
第二步.在主配置中导入该文件
<configuration>
<!--导入properties文件-->
<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="mapper/UserMapper.xml"/>
</mappers>
</configuration>
d.编写Mybatis工具类
package com.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工具类
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory; //创建一个工厂类
/*SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由
丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间
不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。
因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,
最简单的就是使用单例模式或者静态单例模式。*/
static{
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}//初始化该工厂类
public static SqlSession getSqlSession(){
SqlSession sqlsession= sqlSessionFactory.openSession();
return sqlsession;
}
}
//每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
e.创建实体类(@Date可以直接代替get,set,toString,有参,无参构造,需要在配置中引入Maven依赖)
@Data
public class User{
private int id;
private String name;
}
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
f.编写Mapper接口(即数据库所需要完成的操作的接口)
g.Mapper接口的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="org.mybatis.example.BlogMapper">
<select id="selectBlog">
select * from Blog
</select>
</mapper>
!!!其中namespace为该Mapper接口的路径,一定不可以写错,要写上完整路径。
h.测试
二.sql语句
Select语句
<select id="selectPerson" parameterType="int" resultType="hashmap">
SELECT * FROM PERSON WHERE ID = #{id}
</select>
Select 元素的属性:(列举几个常见的)
id: 该语句在命名空间中的唯一标识符。
parameterType: 将会传入这条语句的参数的类全限定名或别名。
resultType: 中返回结果的类全限定名或别名。
resultMap: 对外部 resultMap 的命名引用。
疑惑:
1.resultType和resultMap的区别:
resultType: 指定输出结果的类型,将sql查询结果映射为java对象。
使用resultType时,需要注意此时sql语句的列名必须与指定pojo类的属性名称保持一致,否则无法完成映射。
resultMap: 将sql查询结果映射为java对象。
如果sql语句的列名必须与指定pojo类的属性名称不一致,可以利用将列名和属性名做一个对应关系。对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。
例如:
<select id="selectUser" resultMap="UserMap">
select id , name , pwd from user where id = #{id}
</select>
2、编写resultMap,实现手动映射!
<resultMap id="UserMap" type="User">
<!-- id为主键 -->
<id column="id" property="id"/>
<!-- column是数据库表的列名 , property是对应实体类的属性名 -->
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
对于sql语句中的传参,可以直接在参数前加@Param属性就不需要单独设置参数类型。即不需要单独设置parameterType属性。/*如果参数是 JavaBean , 就不能使用@Param。*/
例如:
Man selectMan(@Param("name") String name);
<select id="selectMan" resultType="Man">
select * from man where name = #{name}
</select>
这里还介绍一个比较实用的方法:在接口类中可以直接使用Map传参。
例如:
Man selectMan(Map<String,Object> map);
<select id="selectMan" parameterType="map" resultType="Man">
select * from man where name = #{name}
</select>
Map<String,Object> map = new HashMap<String,Object>();
map.put("name","姓名");
Man man = mapper.selectMan(map);
Insert语句,Update语句,Delete语句
<insert id="insertAuthor">
insert into Author (id,username,password,email,bio)
values (#{id},#{username},#{password},#{email},#{bio})
</insert>
<update id="updateAuthor">
update Author set
username = #{username},
password = #{password},
email = #{email},
bio = #{bio}
where id = #{id}
</update>
<delete id="deleteAuthor">
delete from Author where id = #{id}
</delete>
!!!记得提交事务!!!
注解开发
使用注解开发可以很好的提高开发效率,利用直接开发就不需要mapper.xml映射文件,直接将sql语句写入接口中。
例如:
1.查询所有用户。
@Select("select * from user where name = #{name}")
User getUser(@Param("name") String name);
2.增加用户。
@Insert("insert into user (id,name,password) values (#{id},#{name},#{password})")
int addUser(User user);
3.修改用户。
@Update("update user set name=#{name},password=#{password} where id = #{id}")
int updateUser(User user);
4.删除用户。
@Delete("delete from user where name = #{name}")
int deleteUser(@Param("name") String name);
三.日志工厂
标准日志(STDOUT_LOGGING)
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
Log4j
推荐使用,通过使用Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI组件....
步骤:
第一步 导入Log4j包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</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
第三步 setting日志设置
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
第四步 使用Log4j进行输出
//注意导包:org.apache.log4j.Logger
static Logger logger = Logger.getLogger(MyTest.class);
@Test
public void selectUser() {
logger.info("info:进入selectUser方法");
logger.debug("debug:进入selectUser方法");
logger.error("error: 进入selectUser方法");
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.selectUser();
for (User user: users){
System.out.println(user);
}
session.close();
}
四.多对一和一对多的相关处理
概述
多对一的理解:
例如: 多个学生对应一个老师。
一对多的理解:
例如:一个老师有多个学生。
处理方法:
处理多对一或者一对多关系时,存在两种方法,一种是根据查询进行嵌套处理,另一种是根据结果嵌套处理。
其中根据查询进行嵌套处理和SQL中的子查询类似;根据结果嵌套处理和SQL中的联表查询类似。
多对一的相关处理
实体类的编写如下:
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;
}
@Data
public class Teacher {
private int id;
private String name;
}
根据查询进行嵌套处理:创建接口和相关的Mapper.xml这里省略,直接对Mapper.xml中的SQL语句进行分析。
<select id="getStudent" resultMap="ST">
select * from student
</select>
<resultMap id="ST" type="Student">
<association property="teacher"
column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="teacher">
select * from teacher where id = #{id}
</select>
疑问:
<resultMap id="ST" type="Student">
<association property="teacher"
column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
这一段代码是什么意思?
association是用来关联两个表的查询。
其中
property:是将下一个名为getTeacher的查询结果传给学生类中的teacher。
column:是查询老师的结果集中,用哪个列的值作为条件去查询关联的对象。
javaType:把sql语句查询出的结果集,封装给哪个类的对象。
根据结果嵌套处理:
<select id="getStudents" resultMap="ST" >
select s.id sid, s.name sname , t.name tname
from student s,teacher t
where s.tid = t.id
</select>
<resultMap id="ST" type="Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
疑问:
result和id的区别:子元素id代表resultMap的主键,而result代表其属性。
一对多的相关处理
实体类的编写如下:
@Data
public class Student {
private int id;
private String name;
private int tid;
}
@Data
public class Teacher {
private int id;
private String name;
private List<Student> students;
}
根据查询嵌套处理:
<select id="getTeacher" resultMap="TS">
select * from teacher where id = #{id}
</select>
<resultMap id="TS" type="Teacher">
<collection property="students" javaType="ArrayList"
ofType="Student" column="id" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from student where tid = #{id}
</select>
疑惑:javaType和ofType的区别:
JavaType和ofType都是用来指定对象类型的,但是JavaType是用来指定pojo中属性的类型,而ofType指定的是映射到list集合属性中pojo的类型 。
根据结果嵌套处理:
<select id="getTeacher" resultMap="TS">
select s.id sid, s.name sname , t.name tname, t.id tid
from student s,teacher t
where s.tid = t.id and t.id=#{id}
</select>
<resultMap id="TS" type="Teacher">
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid" />
<result property="name" column="sname" />
<result property="tid" column="tid" />
</collection>
</resultMap>
</mapper>
五.动态SQL
概述
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
IF
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
如果不传入 “title”,那么所有处于 “ACTIVE” 状态的 BLOG 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果。
choose、when、otherwise
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG。
trim、where、set
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。
<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>
这个“set”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘set’。
这两个标签如果想自行修改的话可以通过trim进行修改。
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
<trim prefix="SET" suffixOverrides=",">
...
</trim>
foreach
<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>
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。
六.一级缓存和二级缓存
概述
缓存就是存在在内存中的临时数据。使用缓存可以减少和数据库的交互次数,从而提高系统的工作效率。
一级缓存
一级缓存也叫本地缓存,我们平常用的就是一级缓存。
一级缓存的特点:
与数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库。
注意:一级缓存是SqlSession级别的缓存,是一直开启的。
二级缓存
二级缓存也叫全据缓存。
二级缓存的特点:
一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;如果当前会话关闭了,一级缓存中的数据被保存到二级缓存中。新的会话查询信息,就可以从二级缓存中获取内容。
使用步骤:
a.在主配置文件中开启全据缓存。
<setting name="cacheEnabled" value="true"/>
b.在每一个mapper.xml中配置使用二级缓存。
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
七.拓展
limit分页
1.编写Mapper接口
List<User> selectUser(Map<String,Integer> map);
2.编写Mapper接口相关的xml文件
<select id="selectUser" parameterType="map" resultType="user">
select * from user limit #{start},#{page}
</select>
3.测试
public void test() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int currentPage = 1; //第几页
int page = 2; //每页显示几个
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("start",(currentPage-1)*page);
map.put("page",page);
List<User> users = mapper.selectUser(map);
for (User user: users){
System.out.println(user);
}
session.close();
}
结果为:从第一页开始,每页显示2个。其中的start是从第几个元素开始,page是每页有几个。
注:该文是一篇学习笔记,内容也比较简单,如若有错误,可私信我,请见谅。谢谢观看。