可以从maven、github里面获得mybatis
连接数据库,编写核心配置文件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>
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
<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=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.kuang.dao"/>
</mappers>
</configuration>
导入MyBatis需要的依赖:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
编写Mybatis工具类
package com.sweet.dao;
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 MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//创建Mybatis第一步:获取sqlsessionfactory对象
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
//SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
// 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
编写代码:
1、实体类
package com.sweet.pojo;
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+
"}";
}
}
2、Dao接口
public interface UserDao {
List<user> getUserList();
}
3、接口实现类由原来的UserDaoImp转换成一个xml接口配置文件,namespace很重要
<?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绑定一个Dao/Mapper接口,resultType表示返回类型,id的值表示Dao/Mapper接口中要实现的方法-->
<mapper namespace="com.sweet.dao.UserDao">
<select id="getUserList" resultType="com.sweet.pojo.user">
select * from mybatis.user;
</select>
</mapper>
4、编写测试类
public class MyTest {
@Test
public void selectUser() {
SqlSession session = MybatisUtils.getSession();
//方法一:
//List<User> users = session.selectList("com.kuang.mapper.UserMapper.selectUser");
//方法二:
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.selectUser();
for (User user: users){
System.out.println(user);
}
session.close();
}
}
5、可能出现问题说明:Maven静态资源过滤问题
在pom.xml中增加:
```
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
CRUD-增删改查
UserMapper.xml
<mapper namespace="com.sweet.dao.UserMapper">
<select id="getUserList" resultType="com.sweet.pojo.user">
select * from mybatis.user
</select>
<select id="getUserId" resultType="com.sweet.pojo.user" parameterType="int">
select * from mybatis.user where id= #{id}
</select>
<insert id="insertUser" parameterType="com.sweet.pojo.user">
insert into mybatis.user (id, NAME, PWD)values (#{id},#{NAME},#{PWD})
</insert>
<update id="updateUser" parameterType="com.sweet.pojo.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>
<insert id="insertUserMap" parameterType="map">
insert into mybatis.user (id, NAME, PWD)values (#{userid},#{username},#{userpwd})
</insert>
public void insertUserMap(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//使用map可以随意设置参数,但是使用user对象就需要全部设置参数
Map<String,Object> hm=new HashMap<String,Object>();
hm.put("userid", 5);
hm.put("username","yue");
hm.put("userpwd",12335362);
mapper.insertUserMap(hm);
sqlSession.commit();
sqlSession.close();
}
void insertUserMap(Map<String,Object> map);
@Test
public void getUserName(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//使用map可以随意设置参数,但是使用user对象就需要全部设置参数
List<user> userName = mapper.getUserName("%李%");
for (user user : userName) {
System.out.println(user);
}
sqlSession.commit();
sqlSession.close();
}
<select id="getUserName" parameterType="String" resultType="com.sweet.pojo.user">
select * from mybatis.user where NAME like #{NAME}
</select>
配置解析
1、核心配置文件
2.环境配置 environments
MyBatis中有两种事务管理器:JDBC和MANAGED,默认是JDBC 数据源DataSource有三种:UNPOOLED|POOLED|JNDI,默认是POOLED
3 属性(properties)
编写db.properties文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true;characterEncoding=utf8;serverTimezone=UTC
user=root
password=123456
在mybatis-config.xml中引入外部配置文件,写在上面
<properties resource="db.properties"/>
4、类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。
它仅用于 XML 配置,意在降低冗余的全限定类名书写。
给实体类起别名:
<typeAliases >
<typeAlias type="com.sweet.pojo.user" alias="user"></typeAlias>
</typeAliases>
指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
<typeAliases >
<package name="com.sweet.pojo"/>
</typeAliases>
5、 设置(settings)
6、映射器(mappers)
7 作用域(Scope)和生命周期
作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题
解决属性名和字段名不一致的问题
解决1:起个别名
解决2、ResultMap
<resultMap id="userMap" type="user">
<!--column是数据库中的属性,property是实体类中的属性-->
<result column="PWD" property="password"/>
</resultMap>
<select id="getUserId" resultType="userMap" parameterType="int">
select id,NAME,PWD as password from mybatis.user where id= #{id}
</select>
日志工厂
在mybatis-config.xml文件中写入:
标准的日志工厂实现:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
Log4j
2、编写log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
1og4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
1og4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
1og4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/sweet.log
log4j.appender.file.MaxFileSize=10mb
1og4j.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
3、在mybatis-config.xml中写入
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
limit实现分页
思考:为什么需要分页? 在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行查询操作,如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。
#语法
`SELECT * FROM table LIMIT stratIndex,pageSize
SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15,从第五行开始,每页10个 `
#为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:
SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.
#如果只给定一个参数,它表示返回最大的记录行数目:
SELECT * FROM table LIMIT 5; //检索前 5 个记录行
#换句话说,LIMIT n 等价于 LIMIT 0,n。
MyBatis步骤:
1、在UserMapper接口中定义:
List<user> getUserByLimit(Map<String,Integer> map);
2、在UserMapper.xml中写sql语句:
<select id="getUserByLimit" resultType="user" parameterType="map">
select * from mybatis.user limit #{firstIndex},#{pageSize}
</select>
3、测试:
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map<String,Integer> map=new HashMap<String,Integer>();
map.put("firstIndex",0);
map.put("pageSize",2);
List<user> userByLimit = userMapper.getUserByLimit(map);
System.out.println(userByLimit);
}
使用注解开发
在真正的开发中,很多时候我们会选择面向接口编程
-
根本原因 : 解耦 , 可拓展 , 提高复用 , 分层开发中 , 上层不用管具体的实现 , 大家都遵守共同的标准 , 使得开发变得容易 , 规范性更好
-
接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
-
- 接口应有两类:
-
- 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
- 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);
-
mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建
-
sql 类型主要分成 :
-
- @select ()
- @update ()
- @Insert ()
- @delete ()
注意: 利用注解开发就不需要mapper.xml映射文件了 .
本质:反射机制实现 底层:动态代理
CRUD
设置:
我们可以在工具类创建的时候自动提交事务!
编写接口,增加注解
基本数据类型前要加@Param,引用数据类型前不加
真正运行的时候,是通过@Param中的参数进行传值的
接口注册千万不能忘!
<mappers>
<mapper class="com.sweet.dao.UserMapper"></mapper>
</mappers>
Lombok
使用注解省略代码
在实体类上加注解
复杂查询(多对一、一对多)
环境搭建:
多对一
按照查询嵌套处理
实体类:
public class Student {
private int id;
private String name;
private Teacher teacher;
}
public class Teacher {
private int id;
private String name;
}
<?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">
//查询所有的学生信息,根据查询到的学生的tid找到老师的信息
<mapper namespace="com.sweet.dao.StudentMapper">
//复杂的属性我们需要单独处理 集合:collections 对象:association
<select id="getStudent" resultMap="StudentTeacher">
select * from mybatis.student
</select>
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id"></result>
<result property="name" column="name"></result>
<association property="teacher" column="tid"
javaType="Teacher" select="getTeacher"></association>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from mybatis.teacher where id=#{tid}
</select>
</mapper>
用到resultMap(将student类中的成员和数据库中的内容一一对应)
用association将复杂的属性,即teacher 和 tid产生联系,通过getTeacher方法
按照结果嵌套处理
<mapper namespace="com.sweet.dao.StudentMapper">
<select id="getStudent2" resultMap="StudentTeacher2">
select s.name sname,s.id sid,t.id tid,t.name tname
from mybatis.student s,mybatis.teacher t
where s.tid=t.id
</select>
<resultMap id="StudentTeacher2" type="student">
<result property="id" column="sid"></result>
<result property="name" column="sname"></result>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"></result>
<result property="id" column="tid"></result>
</association>
</resultMap>
一对多
实体类:
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;
}
按照结果嵌套处理
<mapper namespace="com.sweet.dao.TeacherMapper">
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid,s.name sname,t.id tid,t.name tname
from mybatis.teacher t,mybatis.student s
where s.tid=t.id and t.id=#{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"></result>
<result property="name" column="tname"></result>
<collection property="students" ofType="Student">
<result property="id" column="sid"></result>
<result property="name" column="sname"></result>
<result property="tid" column="tid"></result>
</collection>
</resultMap>
</mapper>
按照查询嵌套处理
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from mybatis.teacher where id=#{tid}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
<result property="id" column="id"></result>
<collection property="students" javaType="ArrayList"
select="getStudentByTeacherId"
ofType="Student" column="id"></collection>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from mybatis.student where id=#{tid}
</select>
工具类
//生成随机ID号
public class IDUtils {
public static String getID(){
return UUID.randomUUID().toString().replaceAll("-","");
}
动态SQL
是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 mapUnderscoreToCamelCase=true
动态SQL——IF
<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>
@Test
public void IFTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Map hm=new HashMap();
//通过不同的参数实现动态搜索的功能
hm.put("title","my paper");
hm.put("author","sweet");
List<blog> blogs = blogMapper.QueryBlogIF(hm);
for (blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
动态SQL——where
如果代码是这样子的:
<select id="QueryBlogIF" parameterType="map" resultType="blog">
select * from mybatis.blog
<if test="title!=null">
title=#{title}
</if>
<if test="author!=null">
and author=#{author}
</if>
</select>
测试时两个参数都写,那么不会报错;
hm.put("title","my paper");
hm.put("author","sweet");
但是如果只写一个参数:
hm.put("author","sweet");
那么查询时就会变成(这样是不符合规范的):
select * from mybatis.blog and author=#{author}
为了解决上述问题,引入了where标签,where标签会自动帮我们添加、删除“and”等连接侧,以及没有参数传入时,自动删除where,即
<select id="QueryBlogIF" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<if test="title!=null">
title=#{title}
</if>
<if test="author!=null">
and author=#{author}
</if>
</where>
</select>
动态SQL——set
(set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)
<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——choose(when,otherwise)
<select id="QueryBlogChoose" resultType="blog" parameterType="map">
select * from mybatis.blog
<where>
<choose>
<when test="title!=null">
title=#{title}
</when>
<when test="author!=null">
and author=#{author}
</when>
<otherwise>
and views=#{views}
</otherwise>
</choose>
</where>
</select>
SQL片段
把一些SQL语句提取出来,组成SQL片段,方便复用
在需要使用的地方引用
foreach
需求:我们需要查询 blog 表中 id 分别为1,2,3的博客信息
1、编写接口
List<Blog> queryBlogForeach(Map map);
2、编写SQL语句
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from blog
<where>
<!--
collection:指定输入对象中的集合属性,通过这个和测试产生联系
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
select * from blog where 1=1 and (id=1 or id=2 or id=3)
-->
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
3、测试
@Test
public void testQueryBlogForeach(){
SqlSession session = MybatisUtils.getSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap map = new HashMap();
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
map.put("ids",ids);
List<Blog> blogs = mapper.queryBlogForeach(map);
System.out.println(blogs);
session.close();
}
缓存
1、什么是缓存 [ Cache ]?
- 存在内存中的临时数据。
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
2、为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
3、什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
- MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
-
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
- 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存
一级缓存也叫本地缓存:
- 与数据库同一次会话期间查询到的数据会放在本地缓存中。
- 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
一级缓存失效的四种情况
一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;
发现发送了两条SQL语句:每个sqlSession中的缓存相互独立
二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
- 工作机制
-
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中;
使用步骤
1、开启全局缓存 【mybatis-config.xml】
<setting name="cacheEnabled" value="true"/>
2、去每个mapper.xml中配置使用二级缓存,这个配置非常简单;【xxxMapper.xml】
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
- 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
- 查出的数据都会被默认先放在一级缓存中
- 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中
用户查询时,1、看二级缓存有没有,2、再看一级缓存,3、如果都没有,查询数据库
自定义缓存 ehcache
Ehcache是一种广泛使用的java分布式缓存,用于通用缓存;
<mapper namespace = “org.acme.FooMapper” >
<cache type = “org.mybatis.caches.ehcache.EhcacheCache” />
</mapper>