Mybatis简介
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
持久层
- 持久化就是将程序在持久和瞬时状态转化过程
为什么需要mybatis
- 传统的JDBC代码太复杂,需要一个框架将其简化
- 帮助程序员将数据存入数据库
开始尝试创建Mybatis
- 创建简单的Maven工程
- 删除src目录,将单纯的Maven目录当做父目录
- 导入依赖(jar包)
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
步骤
- 编写实体类
- 编写核心配置文件
- 编写接口
- 编写Mapper.xml文件
- 测试
核心配置文件
XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)
-
在resources下建立Mybatis-config.xml文件
-
<?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核心配置文件--> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/book?useSSL=true&useUnicode=true&serverTimezone=CST&characterEncoding=utf-8"/> <property name="username" value="root"/> <property name="password" value=""/> </dataSource> </environment> </environments> <!-- <mappers>--> <!-- <mapper resource="org/mybatis/example/BlogMapper.xml"/>--> <!-- </mappers>--> </configuration>
从 XML 中构建 SqlSessionFactory
-
创建一个工具类utils
-
加入静态方法
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (Exception e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession() {//获取SqlSession对象
return sqlSessionFactory.openSession();
}
}
创建实体类
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
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 getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
dao接口
public interface UserDao {
List<User> getUserList();
}
接口实现类由原来的DaoImpl转变为一个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绑定一个Dao/Mapper接口-->
<mapper namespace="com.suwenlong.dao.UserDao">
<select id="getUserList" resultType="com.suwenlong.bean.User">
select * from book.adminstor;
</select>
</mapper>
junit包进行测试
- 在测试的时候我遇到一个问题–Error : java 不支持发行版本5
解决办法一
①在Intellij中点击“File” -->“Project Structure”,看一下“Project”和“Module”栏目中Java版本是否与本地一致:
②点击“Settings”–>“Bulid, Execution,Deployment”–>“Java Compiler”,Target bytecode version设为本地Java版本。(可以在Default Settings中把Project bytecode version 一劳永逸地配置成本地Java版本)
但是最后我运行可以了,不再是这个错误了
解决办法二
①在 maven地址\conf\setting.xml
中设置默认jdk版本…
代码如下:
<profile> <id>development</id> <activation> <jdk>1.8</jdk> <activeByDefault>true</activeByDefault> </activation> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> </properties> </profile>
②并且复制一份到 C:\Users\本地用户名.m2中
对于SQLSessionFactoryBuiler和SQLSessionFactory及SQLSession的官方介绍
NullPointerException有一个错误困扰了我好久,就是MybatisUtils.java文件中的sqlSessionFactory全局变量声明要删除局部变量声明,困扰了我一天了。
CLUD
- 增删改查
namespace
- namespace中的包名要和Mapper接口中的包名一致
Update
Delete
select
- id就是namespace中对应的方法名
- resultType:SQL语句的返回值(结果集)
- parameterType 参数类型(常见类型不写也行)
Insert
- 编写接口
public interface UserMapper { List<User> getUserList();//获取全部用户 User getUserById(int id);//通过id获取用户 int addUser(User user);//插入一个用户 int updateUser(User user);//更新用户 int deleteUser(int id);//更新用户 }
-
编写Mapper中对应的SQL语句
-
<?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"> <!-- namespace绑定一个对应的Mapper接口--> <mapper namespace="com.suwenlong.dao.UserMapper"> <select id="getUserList" resultType="com.suwenlong.bean.User"> select * from mybatis.user; </select> <select id="getUserById" parameterType="int" resultType="com.suwenlong.bean.User"> select * from mybatis.user where id = #{id}; </select> <!-- 对象中的属性。可以直接取用--> <insert id="addUser" parameterType="com.suwenlong.bean.User"> insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{pwd}) </insert> <update id="updateUser" parameterType="com.suwenlong.bean.User"> update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}; </update> <delete id="deleteUser" parameterType="int"> delete from mybatis.user where id=#{id}; </delete> </mapper>
-
test
public class UserDaoTest { @Test public void test(){ SqlSession sqlSession = null; List<User> userList = null; try { //获取SqlSession对象 sqlSession = MybatisUtils.getSqlSession(); //执行SQL //方式一:getMapper UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userList = userMapper.getUserList(); /*// 方式二 List<User> userList1 = sqlSession.selectList("com.suwenlong.dao.UserMapper.getUserList");*/ } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } for (User user : userList) { System.out.println(user); } } @Test public void getUserById(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); sqlSession.close(); } @Test public void addUser(){//增删改需要提交事务 SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int res = mapper.addUser(new User(4, "哈哈哈", "12356")); sqlSession.commit();//提交事务 sqlSession.close(); } @Test public void updateUser(){//增删改需要提交事务 SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int res = mapper.updateUser(new User(4,"updete","123456")); sqlSession.commit();//提交事务 sqlSession.close(); } @Test public void deleteUser(){//增删改需要提交事务 SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int res = mapper.deleteUser(4); sqlSession.commit();//提交事务 sqlSession.close(); } }
模糊查询的两种实现
- java代码查询执行的视乎,传递通配符% %
List<User> userList = mappser.getUserList("%李%")
- 在SQL拼接中使用通配符
select * from mybatis.user where name like %#{values}%
配置
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
环境配置
- mybatis可以配置成适应多种环境
- 不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
- Mybatis默认事务管理器是JDBC,连接池POOLED
属性properties
- 属性可以有外部的配置可以动态替换,也可以在java文件中配置,亦可以通过properties元素的子元素传递。
- 编写一个dp.properties配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&serverTimezone=CST&useUnicode=true&characterEncoding=utf-8
uername=root
password=
在mybatis-config.xml中引入外部文件dp.properties
<properties resource="dp.properties"/>
- 如果两个文件有同一个字段,优先使用外部配置文件dp.properties
类型别名typeAliases
- 类型别名可为 Java 类型设置一个缩写名字。
- 意在降低冗余的全限定类名书写。
<!-- 可以给实体类起一个别名-->
<typeAliases>
<typeAlias type="com.suwenlong.bean.User" alias="User"/>
</typeAliases>
- 也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
- 在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如
domain.blog.Author
的别名为author
<!-- 可以给实体类起一个别名-->
<typeAliases>
<pa type="com.suwenlong.bean" />
</typeAliases>
- 若有注解,则别名为其注解值
- 实体类比较少的情况下可以使用mybatis-config.xml配置别名
- 实体类比较多的情况下可以实体类注解起别名
@Alias("user")
public class User{}
设置
- 这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
映射器 mappers
- 就是注册那玩意
方式一 使用resource
<mappers>
<mapper resource="com/suwenlong/dao/UserMapper.xml"/>
</mappers>
方式二 使用class文件绑定注册
<mappers>
<mapper class="com/suwenlong/dao/UserMapper"/>
</mappers>
- 方式二缺陷
- 接口和他的配置Mapper文件必须同名
- 接口和他的Mapper配置文件必须在同一个目录下
- 方式二好处
- 可以使用注释
方式三 使用扫描包
<mappers>
<package class="com/suwenlong/dao"/>
</mappers>
- 方式三缺陷
- 接口和他的配置Mapper文件必须同名
- 接口和他的Mapper配置文件必须在同一个目录
resultMap 结果集映射
id name pwd
id name password
为了解决从数据库中取出的的数据为null(原因是
实体类中的User的属性与数据库中的属性名不一致
导致查询结果为空)将结果集映射为相同的User实体类
日志 logImpl
- 排错手段 sout debug
- 日志工程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YzX58xWU-1639146255495)(C:\Users\86152\AppData\Roaming\Typora\typora-user-images\image-20211121160634657.png)]
- SLF4J
- LOG4J【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING 【掌握】
- NO_LOGGING
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
LOG4J
- LOG4J 比较麻烦,这里单独说
- 百度百科
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,我们也可以控制每一条日志的输出格式
- 先导入包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 配置log4j.properties配置文件
# priority :debug<info<warn<error
#you cannot specify every priority with different file for log4j
#控制输出
log4j.rootLogger=debug,stdout,info,warn
#console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= [%d{yyyy-MM-dd HH:mm:ss a}]:%p %l%m%n
#info log
log4j.logger.info=info
log4j.appender.info=org.apache.log4j.DailyRollingFileAppender
log4j.appender.info.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.info.File=./src/com/hp/log/info.log
log4j.appender.info.Append=true
log4j.appender.info.Threshold=INFO
log4j.appender.info.layout=org.apache.log4j.PatternLayout
log4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#debug log
log4j.logger.debug=debug
log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender
log4j.appender.debug.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.debug.File=./src/com/hp/log/debug.log
log4j.appender.debug.Append=true
log4j.appender.debug.Threshold=DEBUG
log4j.appender.debug.layout=org.apache.log4j.PatternLayout
log4j.appender.debug.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#warn log
log4j.logger.warn=warn
log4j.appender.warn=org.apache.log4j.DailyRollingFileAppender
log4j.appender.warn.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.warn.File=./src/com/hp/log/warn.log
log4j.appender.warn.Append=true
log4j.appender.warn.Threshold=WARN
log4j.appender.warn.layout=org.apache.log4j.PatternLayout
log4j.appender.warn.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#error
log4j.logger.error=error
log4j.appender.error = org.apache.log4j.DailyRollingFileAppender
log4j.appender.error.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.error.File = ./src/com/hp/log/error.log
log4j.appender.error.Append = true
log4j.appender.error.Threshold = ERROR
log4j.appender.error.layout = org.apache.log4j.PatternLayout
log4j.appender.error.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
- 配置settings
<settings>
<!-- 标准的日志工程实现-->
<setting name="logImpl" value="LOG4J"/>
</settings>
- 直接测试运行
或者编写一个测试类
@Test
public void testLog4j(){
logger.info("info:进入testLog4j");
logger.debug("debug:进入testLog4j");
logger.error("error:进入testLog4j");
}
分页实现查询
-
类似 select * from user limit 0,2;但现在尚且用不到。在这里留下插槽,后面有时间补上
-
首先实现接口类
List<User> getUserByLimit(Map<String,Integer> map);//分页
- 实现Mapper
<!-- 分页查询-->
<select id="getUserByLimit" resultType="User" parameterType="map">
select * from mybatis.user limit #{startIndex},#{pageSize};
</select>
- 编写测试类
@Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> userByLimit = mapper.getUserByLimit(map);
for (User user : userByLimit) {
System.out.println(user);
}
sqlSession.close();
}
使用注解开发
- 了解就行,在接口上直接注解
@Select("select * from user")
- 然后在核心配置文件绑定接口
<mappers>
<mapper class="com/suwenlong/dao/UserMapper"/>
</mappers>
多对一查询
- 说实话没看懂回来抽时间看
一对多查询
- 还没看,抽时间在看
动态SQL
- 动态SQL指的是根据不同的条件生成不同的SQL语句
- 官方介绍如下
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
- if
- choose (when, otherwise)
- trim (where, set)
- 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 '创建时间',
`view` INT(30) NOT NULL COMMENT '阅览量'
)ENGINE=INNODB DEFAULT CHARSET=UTF8
创建一个基础工程
- 导包
- 编写配置文件
- 编写实体类
- 编写实体类对应的Mapper接口和Mapper.xml文件
IF语句
这条语句提供了可选的查找文本功能。如果不传入 “title”,那么所有处于 “ACTIVE” 状态的 BLOG 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果
<select id="queryBlogIF" parameterType="map" resultType="blog">
select * from mybatis.blog where 1=1
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</select>
choose(when,otherwise)
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
- choose必须选一个执行
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<choose>
<when test="title != null">
title=#{title}
</when>
<when test="author != null">
and author=#{author}
</when>
<otherwise>
and view=#{view}
</otherwise>
</choose>
</where>
</select>
trim(where,set)
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
<select id="queryBlogIF" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</where>
</select>
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id = #{id}
</update>
SQL片段
<sql id="if-title-author">
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</sql>
<select id="queryBlogIF" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<include refid="if-title-author"></include>
</where>
</select>
foreach
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!
<!-- select * from mybatis.blog where 1=1 and (id=1 or id=2 or id=3)-->
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
@Test
public void queryBlogForeach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
Blogmapper mapper = sqlSession.getMapper(Blogmapper.class);
HashMap hashMap = new HashMap();
ArrayList<String> ids = new ArrayList<>();
ids.add("6a638b43921b4a3fa720022b3093061c");
ids.add("040d32ef814248cc8d9425566afe991c");
hashMap.put("ids",ids);
List<Blog> blogs = mapper.queryBlogForeach(hashMap);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
动态SQL就是在拼接语句,我们只要保证SQL语句的正确性,按照SQL 的格式,去排列组合就行
缓存
-
因为每次连接数据库都需要消耗资源与时间,但是我们可以把一次查询的结果存在内存中,就不需要每一次都调用数据库了
-
缓存是为了解决高并发问题
缓存失效情况
- 查询不同的东西
- 增删改操作,会改变原来的数据,必定刷新缓存
- 查询不同的Mapper.xml
- 手动清理缓存
sqlSession.cleanCache();//手动清理缓存
实体类最好序列化
- implements Serializable
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gkNebeEs-1639146255497)(C:\Users\86152\AppData\Roaming\Typora\typora-user-images\image-20211122161540294.png)]