环境:jdk mysql maven idea
SSM框架、配置文件的。
一、简介
1.1、什么是Mybatis?
Mysbatis是一款优秀的持久化框架
Mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
Mybatis可以使用简单的XML或注解来配置和映射原生类型,接口和POJO为数据库中的记录
Mybatis原本是apache的一个开源项目Batis2010年这个项目由apache software foundation迁移到了Google code,并改名为Mybati
2013年11月迁移到Github如何获得mybatis?
Maven厂库:
Maven下载网址:https://www.mybatis.org/mybatis-3Maven <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
• <groupId>org.mybatis</groupId>
• <artifactId>mybatis</artifactId>
• <version>3.5.11</version>
</dependency>
Github网址: https://github.com/mybatis/mybatis-3中午网:https://mybatis.net.cn/
1.2、持久化
数据持久化
持久化就是将程序的数据在持久状态和瞬时状态转化的过程
内存断电即失数据库(jdbc)、io文件持久化
为什么需要持久化?
有一些对象,不能丢内存太贵了
13、持久层
Dao层、Service层、Controller层
完成持久化工作的代码块
层界十分明显
1.4、为什么需要Mybatis?
帮助程序员将数据存入数据库中
方便
传统的jdbc太复杂了、简化、框架、自动化
不用mybatis也可以,更容易上手优点:
简单易学
灵活
Sql和代码的分离,提高了可维护性
提供映射标签,支持对象与数据库的orm字段关系映射
提供对象关系标签,支持对象关系组建维护
提供xml标签,支持编写动态sql
*使用的人多
二、创建一个Mybatis项目
1、创建maven项目
2、导入依赖包
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
</dependencies>
这里的mybatis就是mybatis的依赖
3、创建工具类
package com.zeng.mybatis.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;
public class MybatisUtils {
public static 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(){
return sqlSessionFactory.openSession();
}
}
4、创建实体类
package com.zeng.mybatis.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;
}
5、创建Mapper接口
package com.zeng.mybatis.dao;
import com.zeng.mybatis.pojo.User;
import java.util.List;
public interface UserDao {
List<User> getUserList();
User getUserId(int id);
int addUser(User user);
int updateUser(User user);
int deleteUser(int id);
}
6、创建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=\"com.zeng.mybatis.dao.UserDao\">
<select id=\"getUserList\" resultType=\"com.zeng.mybatis.pojo.User\">
select * from User
</select>
</mapper>
7、在resources包下创建连接数据库的对应信息.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>
<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?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false\"/>
<property name=\"username\" value=\"root\"/>
<property name=\"password\" value=\"12345678\"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource=\"com/zeng/mybatis/dao/UserMapper.xml\"/>
</mappers>
</configuration>
三、CRUD
1、namespace
namespace中的包名要和Dao/mapper接口中的包名一致
2、select
选择、查询语句
id:就是对应的namespace中的方法名
resultType:Sql语句执行的返回值!
parameterType:参数类型
2.1、对应例子
Mapper.xml对应标签
<select id=\"getUserId\" resultType=\"com.zeng.mybatis.pojo.User\" parameterType=\"int\">
select * from User where id = #{id}
</select>
@Text测试方法
@Test
public void getUserIdTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
User user = mapper.getUserId(1);
System.out.println(user);
}
3、insert增加标签
Mapper.xml对应标签
<insert id=\"addUser\" parameterType=\"com.zeng.mybatis.pojo.User\">
insert into User (id,name,pwd)
values (#{id},#{name},#{pwd});
</insert>
@Text测试方
@Test
public void addUserTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int row = mapper.addUser(new User(4, \"丁钰\", \"85200\"));
sqlSession.commit();
if (row>0){
System.out.println(\"插入成功\");
}
sqlSession.close();
}
4、update修改标签
Mapper.xml对应标签
<update id=\"updateUser\" parameterType=\"com.zeng.mybatis.pojo.User\">
update User
set name = #{name},pwd = #{pwd}
where id = #{id};
</update>
@Text测试方法
@Test
public void updateUserTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int row = mapper.updateUser(new User(4, \"丁钰儿\", \"7894\"));
sqlSession.commit();
if (row>0){
System.out.println(\"修改成功\");
}
sqlSession.close();
}
5、delete删除标签
Mapper.xml对应标签
<delete id=\"deleteUser\" parameterType=\"int\">
delete
from User
where id = #{id};
</delete>
@Text测试方法
@Test
public void deleteUserTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int row = mapper.deleteUser(4);
sqlSession.commit();
if (row>0){
System.out.println(\"删除成功\");
}
sqlSession.close();
}
一定要记住增删改的时候一定要增加事务
四、配置设置
configuration(配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
1、properties:
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
例如:
<properties resource=\"org/mybatis/example/config.properties\">
<property name=\"username\" value=\"dev_user\"/>
<property name=\"password\" value=\"F2Fa3!33TYyg\"/>
</properties>
设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。比如:
<dataSource type=\"POOLED\">
<property name=\"driver\" value=\"${driver}\"/>
<property name=\"url\" value=\"${url}\"/>
<property name=\"username\" value=\"${username}\"/>
<property name=\"password\" value=\"${password}\"/>
</dataSource>
2、setting
cacheEnabled:全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。true | falsetrue
lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。
logImpl:指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
3、typeAliases:
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<typeAliases>
<typeAlias alias=\"Author\" type=\"domain.blog.Author\"/>
<typeAlias alias=\"Blog\" type=\"domain.blog.Blog\"/>
<typeAlias alias=\"Comment\" type=\"domain.blog.Comment\"/>
<typeAlias alias=\"Post\" type=\"domain.blog.Post\"/>
<typeAlias alias=\"Section\" type=\"domain.blog.Section\"/>
<typeAlias alias=\"Tag\" type=\"domain.blog.Tag\"/>
</typeAliases>
当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name=\"domain.blog\"/>
</typeAliases>每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:
@Alias(\"author\")
public class Author {
...
}
4、mappers:
例如: 用户查找资源(包括
<!-- 使用相对于类路径的资源引用 -->
<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>
5、生命周期和作用域
生命周期,和作用域,之至关重要的,因为错误的使用会导致非常严重的并发问题
SqlSessionFactoryBuilder:
一旦创建了SqlSessionFactory,就不再需要它了
定义局部变量、
SqlSessionFactory:
说白了就是可以想象为:数据库连接池
一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
因此SqlSessionFactory的最佳作用域是应用作用域。
最简单的就是使用单例模式或者静态单例模式。
SqlSession:
连接到连接池的一个请求!
SqlSession的实例不是线程安全的,因此是不能被共享的,所以他的最佳作用域是请求或方法作用域。
用完之后需要赶紧关闭,否则资源被占用!
五、解决字段名和属性名不一致问题
package com.zeng.mybatis.pojo;
import org.apache.ibatis.type.Alias;
public class User {
private int id;
private String name;
private String password;
public User() {
}
public User(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = 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 pwd) {
this.password = pwd;
}
@Override
public String toString() {
return \"User{\" +
\"id=\" + id +
\", name=\'\" + name + \'\\\'\' +
\", pwd=\'\" + password + \'\\\'\' +
\'}\';
}
}
这里可以看到数据库的字段名为pwd而实体类的属性名为password
查询结果:
解决方法:
1、更改sql语句
没改之前
<select id=\"getUserId\" resultType=\"com.zeng.mybatis.pojo.User\" parameterType=\"int\">
select * from User where id = #{id}
</select>
改之后
<select id=\"getUserId\" resultType=\"com.zeng.mybatis.pojo.User\" parameterType=\"int\">
select id,name,pwd as password from User where id = #{id}
</select>
这里我们通过给字段名取别名可以解决这个问题
2、使用resultMap
<?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.zeng.mybatis.dao.UserDao\">
<resultMap id=\"userMap\" type=\"com.zeng.mybatis.pojo.User\">
<result property=\"id\" column=\"id\" />
<result property=\"name\" column=\"name\"/>
<result property=\"password\" column=\"pwd\"/>
</resultMap>
<select id=\"getUserId\" resultMap=\"userMap\">
select * from User where id = #{id}
</select>
</mapper>
这里我们使用resultMap也能查询出来
resultMap
元素是 MyBatis 中最重要最强大的元素。
六、日志工厂
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING |
---|---|---|
1、STDOUT_LOGGING
只需要配置一些文件即可:
<settings>
<setting name=\"logImpl\" value=\"STDOUT_LOGGING\"/>
</settings>
运行方法进行测试:
2、LOG4J
1、导包:
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<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.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/logFile.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=
#日志输出级别 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、设置资源文件
<settings>
<setting name=\"logImpl\" value=\"LOG4J\"/>
</settings>
4、使用Logger类
@Test
public void log4jTest(){
Logger logger = Logger.getLogger(daoTest.class);
logger.info(\"info:进入了log4jTest\");
logger.error(\"error:进入了log4jTest\");
logger.debug(\"debug:进入了log4jTest\");
}
七、分页
1、limit
1、接口
List<User> getUserLimit(Map<String, Integer> map);
2、接口对应的mapper
<select id=\"getUserLimit\" parameterType=\"map\" resultMap=\"userMap\">
select * from User limit #{start},#{end}
</select>
3、测试类
@Test
public void getUserLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
Map<String, Integer> limit = new HashMap<>();
limit.put(\"start\",1);
limit.put(\"end\",2);
List<User> userLimit = mapper.getUserLimit(limit);
for (User user : userLimit) {
System.out.println(user);
}
sqlSession.close();
}
4、结果
2、RowBounds
1、接口
List<User> getUserRowBounds();
2、实现类
<select id=\"getUserRowBounds\" resultMap=\"userMap\">
select * from User
</select>
3、测试
@Test
public void getUserRowBounds(){
RowBounds rowBounds = new RowBounds(1,2);
SqlSession sqlSession = MybatisUtils.getSqlSession();
List<User> userList = sqlSession.selectList(\"com.zeng.mybatis.dao.UserDao.getUserRowBounds\", null, rowBounds);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
3、分页工具
网址:https://pagehelper.github.io/
八、使用注解开发
1、select查询批量数据
1、接口
public interface UserDao {
@Select(\"select * from User\")
List<User> getUserList();
}
2、核心xml配置
(使用注解的核心配置都在这里)
<mappers>
<mapper class=\"com.zeng.mybatis.dao.UserDao\"/>
</mappers>
3、测试
public class daoTest {
@Test
public void getUserList(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
}
2、select根据id查询
1、接口
@Select(\"select * from User where id = #{uid}\")
User getUserById(@Param(\"uid\")int id);
这里的@Param注解是用与有多个基本类型的参数时加上,但是只要时基本数据类型建议都加上
2、测试
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
User userById = mapper.getUserById(1);
System.out.println(userById);
sqlSession.close();
}
3、insert
1、接口
@Insert(\"insert into User (id,name,pwd) values(#{id},#{name},#{pwd})\")
int addUser(User user);
2、测试
@Test
public void addUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
mapper.addUser(new User(6, \"赵云\", \"8520\"));
sqlSession.close();
}
4、update
1、接口
@Update(\"update User set name=#{name},pwd=#{pwd} where id=#{id}\")
int updataUser(User user);
2、测试
@Test
public void updataUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
mapper.updataUser(new User(5,\"叶凡\",\"987654321\"));
sqlSession.close();
}
5、delete
1、接口
@Delete(\"delete from User where id=#{id}\")
int delete(@Param(\"id\")int id);
2、测试
@Test
public void deleteUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
mapper.delete(6);
sqlSession.close();
}
九、Lombok
官方介绍:
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.
Project Lombok是一个java库,可以自动插入编辑器和构建工具,为您的java增添趣味。永远不要再写另一个getter或equals方法,一个注释你的类就有了一个功能齐全的构建器,自动化你的日志变量,等等。
1、IDEA中安装Lombok插件
里面的注解有:
@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
@StandardException
@val
@var
experimental @var
@UtilityClass
2、添加Maven依赖
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
3、使用
package com.zeng.mybatis.pojo;
import lombok.*;
@EqualsAndHashCode //equals和hashcode方法
@ToString //toString方法
@AllArgsConstructor //所有属性的有参构造方法
@NoArgsConstructor //无参构造方法
@Data //注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法
@Getter //加在类上面就是所有属性的get方法加在属性上面可以生成单个get方法
@Setter //加在类上面就是所有属性的set方法加在属性上面可以生成单个set方法
public class User {
private int id;
private String name;
private String pwd;
}
上面的代码等于下面的代码
package com.zeng.mybatis.pojo;
public class User {
private int id;
private String name;
private String password;
public User() {
}
public User(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = 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 pwd) {
this.password = pwd;
}
@Override
public String toString() {
return \"User{\" +
\"id=\" + id +
\", name=\'\" + name + \'\\\'\' +
\", pwd=\'\" + password + \'\\\'\' +
\'}\';
}
}
十、多表查询
1、多对一
1、第一种方式
1、实体类
package com.zeng.mybatis.pojo;
import lombok.Data;
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;
}
package com.zeng.mybatis.pojo;
import lombok.Data;
@Data
public class Teacher {
private int id;
private String name;
}
2、接口
package com.zeng.mybatis.dao;
import com.zeng.mybatis.pojo.Student;
import java.util.List;
public interface studentMapper {
public List<Student> getStudent();
}
package com.zeng.mybatis.dao;
import com.zeng.mybatis.pojo.Teacher;
public interface teacherMapper {
public Teacher getTeacher(int id);
}
3、核心配置文件
<?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>
<properties resource=\"db.properties\"></properties>
<!-- <typeAliases>
<!–<typeAlias type=\"com.zeng.mybatis.pojo.User\" alias=\"user\"></typeAlias>–>
<package name=\"com.zeng.mybatis.pojo\"/>
</typeAliases>-->
<settings>
<setting name=\"logImpl\" value=\"LOG4J\"/>
</settings>
<environments default=\"development\">
<environment id=\"development\">
<transactionManager type=\"JDBC\"/>
<dataSource type=\"POOLED\">
<property name=\"driver\" value=\"${driver}\"/>
<property name=\"url\" value=\"${jdbc.url}\"/>
<property name=\"username\" value=\"${username}\"/>
<property name=\"password\" value=\"${password}\"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class=\"com.zeng.mybatis.dao.studentMapper\"/>
<mapper class=\"com.zeng.mybatis.dao.teacherMapper\"/>
</mappers>
</configuration>
特别注意
mapper是映射接口的时候和mapper映射文件时的区别
接口映射时:
<mappers>
<mapper class=\"com.zeng.mybatis.dao.studentMapper\"/>
<mapper class=\"com.zeng.mybatis.dao.teacherMapper\"/>
</mappers>
mapper映射文件
<mappers>
<mapper class=\"com/zeng/mybatis/dao/studentMapper.xml\"/>
<mapper class=\"com/zeng/mybatis/dao/teacherMapper.xml\"/>
</mappers>
4、接口映射文件
<?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.zeng.mybatis.dao.studentMapper\">
<select id=\"getStudent\" resultMap=\"StudentTeacher\">
select * from Student
</select>
<resultMap id=\"StudentTeacher\" type=\"com.zeng.mybatis.pojo.Student\">
<result property=\"id\" column=\"id\"/>
<result property=\"name\" column=\"name\"/>
<association property=\"teacher\" column=\"tid\" javaType=\"com.zeng.mybatis.pojo.Teacher\" select=\"getTeacher\"/>
</resultMap>
<select id=\"getTeacher\" resultType=\"com.zeng.mybatis.pojo.Teacher\">
select * from teacher where id = #{id}
</select>
</mapper>
5、测试类
@Test
public void getStudentList(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
studentMapper mapper = sqlSession.getMapper(studentMapper.class);
List<Student> student = mapper.getStudent();
for (Student s : student) {
System.out.println(s);
}
sqlSession.close();
}
2、按照结果嵌套查询
1、接口
public List<Student> getStudent2();
2、接口映射文件
<select id=\"getStudent2\" resultMap=\"student2\">
select s.id sid,s.name sname,t.name tname from student s,teacher t where s.tid = t.id
</select>
<resultMap id=\"student2\" type=\"com.zeng.mybatis.pojo.Student\">
<result property=\"id\" column=\"sid\"/>
<result property=\"name\" column=\"sname\"/>
<association property=\"teacher\" javaType=\"com.zeng.mybatis.pojo.Teacher\">
<result property=\"name\" column=\"tname\"/>
</association>
</resultMap>
3、测试类
@Test
public void getStudentList2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
studentMapper mapper = sqlSession.getMapper(studentMapper.class);
List<Student> studentList = mapper.getStudent2();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
1、一对多
1、联合查询ResultMap映射
1、实体类
教师类
package com.mybatis.pojo;
import java.util.List;
public class teacher {
private int id;
private String name;
private List<student> students;
public teacher() {
}
public teacher(int id, String name, List<student> students) {
this.id = id;
this.name = name;
this.students = students;
}
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 List<student> getStudents() {
return students;
}
public void setStudents(List<student> students) {
this.students = students;
}
@Override
public String toString() {
return \"teacher{\" +
\"id=\" + id +
\", name=\'\" + name + \'\\\'\' +
\", students=\" + students +
\'}\';
}
}
学生类
package com.mybatis.pojo;
public class student {
private int id;
private String name;
private int tid;
public student() {
}
public student(int id, String name, int tid) {
this.id = id;
this.name = name;
this.tid = tid;
}
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 int getTid() {
return tid;
}
public void setTid(int tid) {
this.tid = tid;
}
@Override
public String toString() {
return \"student{\" +
\"id=\" + id +
\", name=\'\" + name + \'\\\'\' +
\", tid=\" + tid +
\'}\';
}
}
2、接口
教师接口
package com.mybatis.dao;
import com.mybatis.pojo.teacher;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface teacherMapper {
public List<teacher> getAllTS(@Param(\"stid\") int sid);
}
学生接口
package com.mybatis.dao;
import com.mybatis.pojo.student;
import java.util.List;
public interface studentMapper {
public List<student> getStudent();
}
3、核心配置文件
<?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>
<properties resource=\"db.properties\"></properties>
<settings>
<setting name=\"logImpl\" value=\"LOG4J\"/>
</settings>
<environments default=\"development\">
<environment id=\"development\">
<transactionManager type=\"JDBC\"/>
<dataSource type=\"POOLED\">
<property name=\"driver\" value=\"${driver}\"/>
<property name=\"url\" value=\"${jdbc.url}\"/>
<property name=\"username\" value=\"${username}\"/>
<property name=\"password\" value=\"${password}\"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource=\"com/mybatis/dao/studentMapper.xml\"/>
<mapper resource=\"com/mybatis/dao/teacherMapper.xml\"/>
</mappers>
</configuration>
接口映射文件
teacherMapper
<?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.mybatis.dao.teacherMapper\">
<resultMap id=\"teacherMap\" type=\"com.mybatis.pojo.teacher\">
<result property=\"id\" column=\"tid\" />
<result property=\"name\" column=\"tname\"/>
<collection property=\"students\" ofType=\"com.mybatis.pojo.student\">
<result property=\"id\" column=\"sid\"/>
<result property=\"name\" column=\"sname\"/>
<result property=\"tid\" column=\"stid\"/>
</collection>
</resultMap>
<select id=\"getAllTS\" resultMap=\"teacherMap\">
select t.id tis,t.name tname,s.id sid,s.name sname from teacher t,student s where t.id = s.tid and t.id = #{stid}
</select>
</mapper>
4、测试类
@org.junit.Test
public void getAllTS(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
teacherMapper mapper = sqlSession.getMapper(teacherMapper.class);
List<teacher> allTS = mapper.getAllTS(1);
for (teacher teacher : allTS) {
System.out.println(teacher);
}
sqlSession.close();
}
2、子查询映射
1、接口
List<teacher> getTeacherS(@Param(\"tid\") int tid);
2、接口映射文件
<select id=\"getTeacherS\" resultMap=\"teacherMaps\">
select * from teacher where id=#{tid}
</select>
<resultMap id=\"teacherMaps\" type=\"com.mybatis.pojo.teacher\">
<collection property=\"students\" javaType=\"ArrayList\" ofType=\"com.mybatis.pojo.student\" select=\"getStudents\" column=\"id\"/>
</resultMap>
<select id=\"getStudents\" resultType=\"com.mybatis.pojo.student\">
select * from student where tid=#{tid}
</select>
3、测试类
@org.junit.Test
public void getTeacherS(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
teacherMapper mapper = sqlSession.getMapper(teacherMapper.class);
List<teacher> teachers = mapper.getTeacherS(1);
for (teacher teacher : teachers) {
System.out.println(teacher);
}
sqlSession.close();
}
十一、动态SQL
1、动态SQL环境搭建
1、创建数据库
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 \'创建时间\',
`views` INT(30) NOT NULL COMMENT \'浏览量\'
)ENGINE=INNODB DEFAULT CHARSET=utf8;`blog`
2、创建实体类
package com.zeng.mybatis.pojo;
import lombok.Data;
import java.util.Date;
@Data
public class Blog {
private String id;
private String title;
private String author;
private Date create_time;
private int views;
}
3、db.properties
driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
username=root
password=12345678
4、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>
<properties resource=\"db.properties\"></properties>
<settings>
<setting name=\"logImpl\" value=\"LOG4J\"/>
<setting name=\"mapUnderscoreToCamelCase\" value=\"true\"/>
</settings>
<environments default=\"development\">
<environment id=\"development\">
<transactionManager type=\"JDBC\"/>
<dataSource type=\"POOLED\">
<property name=\"driver\" value=\"${driver}\"/>
<property name=\"url\" value=\"${jdbc.url}\"/>
<property name=\"username\" value=\"${username}\"/>
<property name=\"password\" value=\"${password}\"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class=\"com.zeng.mybatis.dao.BlogMapper\"/>
</mappers>
</configuration>
5、工具类MybatisUtil
package com.zeng.mybatis.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;
public class MybatisUtils {
public static 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(){
return sqlSessionFactory.openSession(true);
}
}
6、生成ID工具类IDUtil
package com.zeng.mybatis.utils;
import java.util.UUID;
public class IDUtil {
public static String getId(){
return UUID.randomUUID().toString().replaceAll(\"-\",\"\");
}
}
7、接口
package com.zeng.mybatis.dao;
import com.zeng.mybatis.pojo.Blog;
import java.util.List;
public interface BlogMapper {
int addBlog(Blog blog);
}
8、接口映射
<?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.zeng.mybatis.dao.BlogMapper\">
<insert id=\"addBlog\" parameterType=\"com.zeng.mybatis.pojo.Blog\">
insert into blog (id,title,author,create_time,views) values (#{id},#{title},#{author},#{create_time},#{views})
</insert>
</mapper>
9、生成数据
package com.zeng.mybatis.dao;
import com.zeng.mybatis.pojo.Blog;
import com.zeng.mybatis.utils.IDUtil;
import com.zeng.mybatis.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.Date;
public class Test {
@org.junit.Test
public void addtest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IDUtil.getId());
blog.setTitle(\"Mybatis学习\");
blog.setAuthor(\"曾老师\");
blog.setCreate_time(new Date());
blog.setViews(9000);
mapper.addBlog(blog);
Blog blog1 = new Blog();
blog1.setId(IDUtil.getId());
blog1.setTitle(\"java学习\");
blog1.setAuthor(\"曾老师\");
blog1.setCreate_time(new Date());
blog1.setViews(8000);
mapper.addBlog(blog1);
Blog blog2 = new Blog();
blog2.setId(IDUtil.getId());
blog2.setTitle(\"javaWeb学习\");
blog2.setAuthor(\"曾老师\");
blog2.setCreate_time(new Date());
blog2.setViews(100000);
mapper.addBlog(blog2);
Blog blog3 = new Blog();
blog3.setId(IDUtil.getId());
blog3.setTitle(\"单片机\");
blog3.setAuthor(\"曾老师\");
blog3.setCreate_time(new Date());
blog3.setViews(4000);
mapper.addBlog(blog3);
Blog blog4 = new Blog();
blog4.setId(IDUtil.getId());
blog4.setTitle(\"人工智能学习\");
blog4.setAuthor(\"曾老师\");
blog4.setCreate_time(new Date());
blog4.setViews(500);
mapper.addBlog(blog4);
Blog blog5 = new Blog();
blog5.setId(IDUtil.getId());
blog5.setTitle(\"大数据\");
blog5.setAuthor(\"曾老师\");
blog5.setCreate_time(new Date());
blog5.setViews(1500);
mapper.addBlog(blog5);
sqlSession.close();
}
}
2、IF标签
1、接口
List<Blog> getBlogIF(Map map);
2、接口映射
<select id=\"getBlogIF\" parameterType=\"map\" resultType=\"com.zeng.mybatis.pojo.Blog\">
select * from blog where 1=1
<if test=\"title != null\">
and title = #{title}
</if>
</select>
3、测试类
@org.junit.Test
public void getBlogIF(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap<>();
map.put(\"title\",\"单片机\");
List<Blog> blogs = mapper.getBlogIF(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
3、choose(when、otherwise)
1、接口
List<Blog> getBlogChoose(Map map);
2、接口映射
<select id=\"getBlogChoose\" parameterType=\"map\" resultType=\"com.zeng.mybatis.pojo.Blog\">
select * from blog where 1=1
<choose>
<when test=\"title != null\">
and title = #{title}
</when>
<when test=\"author != null\">
and author = #{author}
</when>
<otherwise>
and views >= #{views}
</otherwise>
</choose>
</select>
3、测试类
@org.junit.Test
public void getBlogChoose(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap Map = new HashMap<>();
Map.put(\"author\",\"曾老师\");
Map.put(\"views\",8000);
List<Blog> blogs = mapper.getBlogChoose(Map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
4、trim(where、set)
1、where
1、接口
List<Blog> getBlogWhere(Map map);
2、接口映射
<select id=\"getBlogWhere\" resultType=\"com.zeng.mybatis.pojo.Blog\" parameterType=\"map\">
select * from blog
<where>
<choose>
<when test=\"title != null\">
title = #{title}
</when>
<when test=\"author != null\">
and author = #{author}
</when>
</choose>
</where>
</select>
3、测试类
@org.junit.Test
public void getBlogWhere(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap<>();
map.put(\"title\",\"人工智能学习\");
List<Blog> blogs = mapper.getBlogWhere(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
2、set
1、接口
int modifyBlogSet(Map map);
2、接口映射
<update id=\"modifyBlogSet\" parameterType=\"map\">
update blog
<set>
<if test=\"title != null\">
title = #{title},
</if>
<if test=\"author != null\">
author = #{author}
</if>
</set>
<where>
<if test=\"id != null\">
id = #{id}
</if>
</where>
</update>
3、测试类
@org.junit.Test
public void modifyBlog(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap<>();
map.put(\"id\",\"49aa2e7503434b86a33c527cf520e6a9\");
map.put(\"title\",\"单片机1\");
map.put(\"author\",\"超级大帅哥\");
mapper.modifyBlogSet(map);
sqlSession.close();
}
3、trim
1、接口
List<Blog> getBlogTrim(Map map);
2、接口映射
<select id=\"getBlogTrim\" parameterType=\"map\" resultType=\"com.zeng.mybatis.pojo.Blog\">
select * from blog
<trim prefix=\"where\" suffixOverrides=\"and | or\">
<if test=\"title != null\">
title = #{title}
</if>
<if test=\"author != null\">
and author = #{author}
</if>
</trim>
</select>
3、测试
@org.junit.Test
public void getBlogTrim(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap<>();
map.put(\"title\",\"单片机1\");
map.put(\"author\",\"超级大帅哥\");
List<Blog> blogs = mapper.getBlogTrim(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
十二、缓存
1、缓存简介
缓存(cache),原始意义是指访问速度比一般
1、什么是缓存?
存在内存中的临时数据
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题
2、为什么使用缓存?
减少和数据库的交互次数,减少系统开销,提高系统效率
3、什么样的数据能使用缓存?
经常查询并且不经常改变的数据
2、一级缓存
一级缓存也叫本地缓存:Sqlsession
与数据库同一次会话期间查询到的数据会放在本地缓存中
以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
测试步骤:
1、开启日志!
导入log4j依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
<scope>test</scope>
</dependency>
配置日志文件
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/logFile.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
2、测试在一个Session中查询两次相同记录
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
userMapper mapper = sqlSession.getMapper(userMapper.class);
user user = mapper.getUserById(1);
System.out.println(user);
System.out.println(\"<==========================================================>\");
user user2 = mapper.getUserById(1);
System.out.println(user2);
sqlSession.close();
}
3、查看日志输出
可以清楚的看见只运行了一次sql语句
缓存失效的情况:
1、查询不同的东西
这里分别查询id为1和2的两条记录
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
userMapper mapper = sqlSession.getMapper(userMapper.class);
user user = mapper.getUserById(1);
System.out.println(user);
System.out.println(\"<==========================================================>\");
user user2 = mapper.getUserById(2);
System.out.println(user2);
sqlSession.close();
}
这里可以看见运行了两次sql语句
2、增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
这里我们更改了第一条记录,并且对id为1的记录查询两次
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
userMapper mapper = sqlSession.getMapper(userMapper.class);
user user = mapper.getUserById(1);
System.out.println(user);
System.out.println(\"<==========================================================>\");
user setUser = new user();
setUser.setId(1);
setUser.setName(\"张三(改)\");
setUser.setPwd(\"8520\");
mapper.updateUser(setUser);
System.out.println(\"<==========================================================>\");
user user1 = mapper.getUserById(1);
System.out.println(user1);
sqlSession.close();
}
这里可以清楚的看见在第一次查询过后,对记录进行更改,然后再次查询也会再执行一次sql语句,所以我们在执行增删改的时候会更新数据,因此会重新连接查询
3、查询不同的Mapper.xml
4、手动清除缓存
这里我们使用clearCache()方法来手动清除缓存
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
userMapper mapper = sqlSession.getMapper(userMapper.class);
user user = mapper.getUserById(1);
System.out.println(user);
System.out.println(\"<==========================================================>\");
sqlSession.clearCache();//手动清楚缓存
user user1 = mapper.getUserById(1);
System.out.println(user1);
sqlSession.close();
}
这里我们可以看到在使用clearCache()方法,手动清除缓存后就会对数据就行第二次连接查询
3、二级缓存
二级缓存也叫全局缓存,一级缓存作用域太低,所以诞生了二级缓存
基于namespace级别的缓存,一个名称空间,对应一个二级缓存
工作机制
一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据会被保存到二级缓存中;
新的会话查询信息,就可以从二级缓存中获取内容;
不同的mapper查出的数据会放在自己对应的缓存(map)中
步骤:
1、开启全局缓存
<settings>
<!--开启二级缓存-->
<setting name=\"cacheEnabled\" value=\"true\"/>
</settings>
2、在要使用二级缓存的Mapper中开启
<cache/>
直接加一个<cache/>标签就可以开启二级缓存了
<cache
eviction=\"FIFO\"
flushInterval=\"60000\"
size=\"512\"
readOnly=\"true\"/>
当然里面可以配置各种参数,参数可以自行配置
3、测试
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
SqlSession sqlSession2 = MybatisUtils.getSqlSession();
userMapper mapper = sqlSession.getMapper(userMapper.class);
user user = mapper.getUserById(2);
System.out.println(user);
sqlSession.close();
System.out.println(\"<======================================================================>\");
userMapper mapper2 = sqlSession2.getMapper(userMapper.class);
user user1 = mapper2.getUserById(2);
System.out.println(user1);
sqlSession2.close();
}
问题
org.apache.ibatis.cache.CacheException: Error serializing object. Cause: java.io.NotSerializableException: com.mybatis.pojo.user
没有参数就要序列化实体类
可以看出在会话一关闭后,数据就存入了二级缓存,所有第二次查询相同数据的时候没有走数据库,而是直接在二级缓存中取出
只要开启了二级缓存,在同一个Mapper下就有效
所有的数据都会先放在一级缓存中;
只有当会话提交,或者关闭的时候,才会提交到二级缓冲中!
4、缓存原理
一级缓存是SqlSession级别的缓存,默认开启。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
二级缓存是mapper级别的缓存,基于mapper文件的namespace,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
一级缓存先在数据库中查询数据,然后存入二级缓存中,如果二级缓存中没有再从数据库中查找
5、Ehcahe
什么是EhCache?
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是
来源:https://www.cnblogs.com/littlenoob/p/17262897.html
本站部分图文来源于网络,如有侵权请联系删除。