【开发】后端框架——Mybatis
前置知识:JDBC
学习视频
Mybatis——一种ORM框架:将Java中的Bean映射为数据库的记录
- ORM:用于实现面向对象编程语言里不同类型系统的数据之间的转换
Mybatis运行过程——工厂模式
#{}
与 ${}
——三点区别
动态Sql
分页——两种方法
缓存——查询缓存顺序
MyBatis
文档
官方文档
下载链接
sql相关
- sql引擎
- innoDB底层
- 索引
- 索引优化
概述
JDBC
JDBC驱动程序:JDBC(Java Database Connectivity, Java 数 据 库 连 接)是 一 种可用于执行 SQL 语句的 Java API(Application Programming Interface)
实现了从 Java 程序内调用标准的
SQL命令
对数据库进行查询、插入、删除和更新等操作, 并确保数据事务的正常进行基本层次结构由 Java 程序、JDBC 驱动程序管理器、数据库驱动程序和数据库四部分组成
Java 程序依赖于 JDBC API,通过
DriverManager
来获取驱动,并且针对不同的数据库可以使用不同的驱动。这是典型的桥接的设计模式,把
抽象 Abstraction
与行为实现Implementation 分离
开来,从而可以保持各部分的独立性以及应对他们的功能扩展。
JDBC步骤
public static void connectionTest(){Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {// 1. 加载并注册 MySQL 驱动器实例Class.forName("com.mysql.cj.jdbc.Driver").newInstance();// 2. 将Mysql驱动程序注册到驱动管理程序中// 根据特定的数据库连接URL,返回与此URL所匹配的数据库驱动对象Driver driver = DriverManager.getDriver("jdbc:mysql://localhost:3306/[dbName]"); // 3. 传入参数,比如说用户名和密码Properties props = new Properties();props.put("user", USER_NAME);props.put("password", PASSWORD);// 4. 使用数据库驱动创建数据库连接 Connectionconnection = driver.connect(URL, props);// 5. 从数据库连接 connection 中获得 Statement 对象statement = connection.createStatement();// 6. 执行 sql 语句,返回结果resultSet = statement.executeQuery("select * from activity");// 7. 处理结果,取出数据while(resultSet.next()){System.out.println(resultSet.getString(2));}.....}finally{// 8.关闭链接,释放资源 按照JDBC的规范,使用完成后管理链接,// 释放资源,释放顺序应该是: ResultSet ->Statement ->ConnectionresultSet.close();statement.close();connection.close();}
}
JDBC存在的问题
- 传统的JDBC代码复杂
- 实现步骤多
- 需要设计一种 将数据映射到数据库的框架 来简化JDBC的步骤
Mybatis特点
持久层框架
持久化:将程序中的数据从 瞬时状态【内存:断电即失】转化为 持久状态【数据库jdbc,io文件持久化】的过程
持久层:完成数据持久化工作的代码块,层次间界限分明
定制化Sql
避免JDBC代码,手动设置参数和获取结果集
优点
- 简单:两个jar文件(mybatis.jar+mysql-connector.jar)+配置几个sql映射文件(Mapper)
- 灵活:sql写在xml里,统一管理(mybatis-config.xml)
- 解除sql与程序的耦合:通过提供Mapper层,将业务逻辑与数据访问逻辑分离
- 提供映射标签:JavaBean与数据库字段的关系映射
- 提供xml标签,支持编写动态sql
Mybatis执行流程
通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO
- 获取配置文件
- 实例化
SqlSessionFactoryBuilder
- 加载 mybatis-config.xml 中的配置信息
- 实例化
SqlsessionFactory
- 创建执行器
executor
- 创建
SqlSession
- 实现CRUD逻辑
- CRUD后提交事务,判断是否执行成功
1. 导包——Maven
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version>
</dependency>
SqlSessionFactoryBuilder
一旦创建了 SqlSessionFactory
,就不再需要它了。
因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域
- 作用域:局部变量
SqlSessionFactory <=> 数据库连接工厂
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心
构造方法:从xml中配置文件中构建SqlSessionFactory实例
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在
- 作用域:应用作用域
最简单的就是使用单例模式或者静态单例模式。
SqlSession => JDBC:Connection对象
通过 SqlSessionFactory
获得 SqlSession
的实例。
SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
SqlSession的实例时线程不安全的,不能被共享
- 每次收到一个数据库访问请求,打开一个SqlSession,返回响应后,立即关闭
Mapper => JDBC:Statement
代理对象 执行具体业务
将接口与xml进行绑定
Mybatis示例程序
1. 搭建数据库
create table user(id int(11) not null primary key,user_name varchar(30) default null,pwd varchar(30) default null
)engine=InnoDB default charset=utf8;insert into user(id,user_name,pwd) values
(1,'a','123456'),
(2,'b','123456'),
(3,'c','c123456');
2. 新建项目
3. 删除src,使项目成为父工程
4. maven导入依赖
<!-- 父工程 -->
<groupId>com.kuang.MyBatis</groupId>
<artifactId>MyBatis</artifactId>
<version>1.0-SNAPSHOT</version><!-- 导入依赖 -->
<dependencies><!-- 导入MySql驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.16</version></dependency><!-- 导入MyBatis驱动 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version></dependency><!-- 导入junit依赖 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency>
</dependencies>
5. 新建模块
6. 获取数据库连接对象
a. 编写核心配置文件——mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- configuration的核心配置 -->
<configuration><!-- 可配置多套环境,defaulte属性选择当前属性 --><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/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8"/><property name="username" value="root"/><property name="password" value="2017002231"/></dataSource></environment></environments><!-- 每一个Mapper.xml都需要在MyBatis核心配置文件中注册 --><mappers><mapper resource="com/kuang/dao/UserMapper.xml"/></mappers>
</configuration>
- useSSL:使用安全连接
- useUnicode:保证中文不乱码
- characterEncoding:编码格式
b. 编写MyBatis工具类
新建dao包,utils包
public class MyBatisUtils {private static SqlSessionFactory sqlSessionFactory;// 1.获取SqlSessionFactory对象static{try {//1. 将资源中的配置文件以流的形式读入String resource = "mybatis-config.xml";InputStream configuration = Resources.getResourceAsStream(resource);//2. 通过工厂类构建器构建SqlSessionFactory类sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);} catch (IOException e) {e.printStackTrace();}}// 2.获取SqlSession对象public static SqlSession getSqlSession(){return sqlSessionFactory.openSession();}
}
7. 编写代码
实体类
pojo的作用就是将从数据库获取到的数据封装为一个一个的对象,让java能够更好的进行操作DO、VO
package com.kuang.pojo;public class User {private Integer id;private String user_name;private String pwd;public User(){}public User(Integer id, String user_name, String pwd) {this.id = id;this.user_name = user_name;this.pwd = pwd;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUser_name() {return user_name;}public void setUser_name(String user_name) {this.user_name = user_name;}public String getPwd() {return pwd;}public void setPwd(String pwd) {this.pwd = pwd;}@Overridepublic String toString() {return "User{" +"id=" + id +", user_name='" + user_name + '\'' +", pwd='" + pwd + '\'' +'}';}
}
Dao接口
public interface UserDao{List<User> getUserList();
}
接口的实现
由 UserDaoImpl 转化为 Mapper 配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- 绑定一个对应的Dao/Mapper接口 -->
<mapper namespace="com.kuang.dao.UserDao"><!--id:方法名--><select id="getUserList" resultType="com.kuang.pojo.User">select *from mybatis.user;</select>
</mapper>
- namespace:相当于指定要实现的接口
- 将不同的语句隔离开来,同时也实现了接口绑定
- 全限定名(比如 “com.mypackage.MyMapper.selectAllThings)将被直接用于查找及使用
- 短名称(比如 “selectAllThings”)如果全局唯一也可以作为一个单独的引用。 如果不唯一,有两个或两个以上的相同名称(比如 “com.foo.selectAllThings” 和 “com.bar.selectAllThings”),那么使用时就会产生“短名称不唯一”的错误,这种情况下就必须使用全限定名。
- 将不同的语句隔离开来,同时也实现了接口绑定
- id:方法名
- resultType:返回单个
- resultMap:返回多个
8. 测试
新建 测试类
编写测试代码
package com.kuang.dao;import com.kuang.pojo.User;
import com.kuang.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;import java.util.List;public class UserDaoTest {@Testpublic void test(){//1.获取SqlSession对象SqlSession sqlSession = MyBatisUtils.getSqlSession();//2.执行Sql
//通过反射机制,获取方法区中UserMapper的Class类实例,这个实例中有UserMapper全部信息UserMapper userMapper = sqlSession.getMapper(UserMapper.class);List<User> userList = userMapper.getUserList();/*//方式二:强制类型转换,不安全List<User> userList = sqlSession.selectOne("com.kuang.UserDao.getUserList");*/for (User user:userList){System.out.println(user);}//关闭sqlSessionsqlSession.close();}
}
遇到的各种错误
org.apache.ibatis.io不存在——IDEA2020.1
class not found:ClassTest
执行UserDaoTest的test方法之前,要先 mvn test-compile 生成 test classes才可被部署并发现
Type interface com.kuang.dao.UserDao is not known to the MapperRegistry
<!-- 每一个Mapper.xml都需要在MyBatis核心配置文件中注册 -->
<mappers><mapper resource="com/kuang/dao/UserMapper.xml"/>
</mappers>
Could not find resource com/kuang/dao/UserMapper.xml
Maven约定大于配置,自己写的配置文件默认不会被导出或生效
Maven默认的资源(自己配置的xml)位置在resources目录下,当前项目的xml位于java目录下,所以找不到
<!-- build中配置resources,防止资源导出失败问题 -->
<build><resources><resource><!-- 使得directory目录下的资源可以被导出 --><directory>src/main/java</directory><!-- 设置可被识别通过的文件类型 --><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource><resource><!-- 使得directory目录下的资源可以被导出 --><directory>src/main/resources</directory><!-- 设置可被识别通过的文件类型 --><includes><include>**/*.propertes</include><include>**/*.xml</include></includes><filtering>true</filtering></resource></resources>
</build>
终于成功
增删改查CRUD
CUD 需要通过connection对象以Transition(事务)的形式提交
- 编写接口
- (实现接口)编写mapper中对应的sql语句
- 测试
select
选择,查询语句:
- id:方法名
- resultType:Sql语句执行的返回值
- parameterType:参数类型
通过id获取用户
模糊查询
Java代码执行时,传递通配符
%%
在sql语句拼接中使用通配符
- 存在sql注入的风险
insert
并没有新增
更新
删除
参数传递&Map
单个参数传递方式
- 只有一个 基本数据类型 ,可省略
- 实体类对象作为参数,sql语句中的参数取对象的属性
- Map作为参数,sql语句中参数取Map的属性
多个参数的传递
使用Map的情况
当字段过多时,考虑使用 Map,可以自定义需要传递的参数
- 若使用实体类作为参数传递,当字段过多时,一个实体类的每个属性都必须设置值
Plugin——通用Mapper
核心配置——mybatis-config.xml
configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- mappers(映射器)
属性(properties)
通过Properties引用配置文件
- 优先使用外部配置文件
- properties文件定义属性的键值对——定义变量
#db.properties
mysql_driver=com.mysql.cj.jdbc.Driver
#不需要转义&
MyBatis_url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8
db_username=root
db_pwd=2017002231
通过
<properties/>
声明引用属性<!-- properties引用属性变量 --> <properties resource="db.properties"/>
使用属性
设置Settings
Setting | Description | Valid Values | Default |
---|---|---|---|
cacheEnabled | 是否缓存Globally enables or disables any caches configured in any mapper under this configuration. | true | false | true |
lazyLoadingEnabled |
懒加载,提高开发效率When enabled, all relations will be lazily loaded. This value can be superseded for a specific relation by using the fetchType attribute on it.
|
true | false | false |
mapUnderscoreToCamelCase | Enables automatic mapping from classic database column names A_COLUMN to camel case classic Java property names aColumn. | true | false | false |
logImpl | MyBatis的日志实现方式Specifies which logging implementation MyBatis should use. | LOG4J|STDOUT_LOGGING | No Set |
日志实现——logImpl
- SLF4J
- LOG4J | LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING
- NO_LOGGING
STDOUT_LOGGING
<!--设置日志实现方式-->
<settings><setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
- 由日志可见,MyBatis底层是基于JDBC实现的
LOG4J
- 控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等
- 控制每一条日志的输出格式
导入
LOG4J
包<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version> </dependency>
配置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 = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n# 文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File = ./logs/log.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold = DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %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
程序中使用log4j
导包
import org.apache.log4j.Logger;
设置变量
static Logger logger = Logger.getLogger(UserMapperTest.class);
Logger.getLogger(className):将日志对象与目标对象绑定
使用
log级别
- info([信息])
- debug([信息])
- error([信息])
环境配置 enviroments
MyBatis 可以配置成适应多种环境,但每个 SqlSessionFactory 实例只能选择一种环境
如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。
- 默认使用的环境 ID(比如:default=“development”)。
- 每个 environment 元素定义的环境 ID(比如:id=“development”)。
- 事务管理器的配置(比如:type=“JDBC”)。
- 数据源的配置(比如:type=“POOLED”)。
事务管理器(transactionManager)
两种类型的事务管理器(也就是 type=“[JDBC|MANAGED]”)
- 使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
数据源(dataSource)
连接数据库:
- jdbc
- dbcp
- c3p0
- druid
三种数据源类型
UNPOOLED——用完即销毁
无连接池,每次请求时打开和关闭连接
浪费资源
POOLED——用完即回收
JNDI
类型别名typeAliases
用于减少完全限定名的冗余给Bean取别名
配置方式
实体类较少,可逐一指定
<!-- mybatis-config.xml --> <typeAliases><typeAlias type="com.kuang.pojo.User" alias="User"/> </typeAliases><!-- UserMapper.xml --> <mapper namespace="com.kuang.mapper.UserMapper"><!--id:方法名--><select id="getUserList" resultType="User">select *from mybatis.user;</select> </mapper>
You can also specify a package where MyBatis will search for all beans.包中实体类的别名为 lowercase(类名的首字母)
<!-- mybatis-config.xml --> <typeAliases><package name="com.kuang.pojo" /> </typeAliases><!-- UserMapper.xml --> <mapper namespace="com.kuang.mapper.UserMapper"><!--id:方法名--><select id="getUserList" resultType="user">select *from mybatis.user;</select> </mapper>
If the
@Alias
annotation is found its value will be used as an alias. 优先级 :注解别名>配置别名
MyBatis默认配置的别名
Alias | Mapped Type |
---|---|
_普通数据类型
|
普通数据类型(int,short,long,byte,double,float,boolean) |
小写首字母(数据类型名) | 首字母大写的数据类型(基本数据类型 + Date,Object,Map,HashMap,List,ArrayList,Collection,Iterator) |
Integer,int | BigDecimal |
decimal,bigdecimal | Integer |
插件plugins
mybatis-generator-core
mybatis-plus
通用mapper
映射器mapper
接口实现(mapper.xml)必须在configuration中注册才可被发现
第一种方式:资源路径【推荐】
<!-- Using classpath relative resources -->
<mappers><mapper resource="org/mybatis/builder/AuthorMapper.xml"/><mapper resource="org/mybatis/builder/BlogMapper.xml"/><mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
第二种方式:类名
<!-- Using mapper interface classes -->
<mappers><mapper class="org.mybatis.builder.AuthorMapper"/><mapper class="org.mybatis.builder.BlogMapper"/><mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
第三种方式:包内全导入
<!-- Register all interfaces in a package as mappers -->
<mappers><package name="org.mybatis.builder"/>
</mappers>
第二、三种方式的问题
- 接口和Mapper配置文件必须同名
- 接口和Mapper配置资源必须在同一包下
Mapper.xml(Dao实现类)
resultMap
– The most complicated and powerful element that describes how to load your objects from the database result sets.- javaType:class——POJO| ArrayList
- ofType:list 或 set 中的POJO
insert
– A mapped INSERT statement.update
– A mapped UPDATE statement.delete
– A mapped DELETE statement.select
– A mapped SELECT statement.
解决属性名和字段名不一致问题——resultMap
简单的例子
public class User {private Integer id;private String user_name;private String password;
}
解决思路:起别名
在 sql
中,用 as
关键字,可以给某一字段起别名
select *from user where id=#{id}selectid as id,user_name as user_name,pwd as password
from user where id=#{id};
在 Mybatis
中,使用 ResultMap
做结果映射,只需要将有差异的属性与字段映射即可
- property:POJO中的属性
- column:数据库中的字段
复杂查询
实体间的复杂关系&环境搭建
关联association:多对一
- 查到的是结果是满足某种关系的个体集
集合collection:一对多
- 返回的结果是一个个体,其中某个属性是个体集
create table teacher(id int(10) not null,user_name varchar(30) default null,primary key(id)
)engine=InnoDB default charset=utf8;insert into teacher(id,user_name) values(1,'秦老师');
insert into teacher(id,user_name) values(2,'江老师');create table student(id int(10) not null,user_name varchar(30) default null,tid int(10) default null,primary key (id),key fktid(tid),constraint fktid foreign key (tid) references teacher(id)
)engine=InnoDB default charset=utf8;insert into student(id,user_name,tid) values(1,"小明",1);
insert into student(id,user_name,tid) values(2,"小红",2);
insert into student(id,user_name,tid) values(3,"小张",1);
insert into student(id,user_name,tid) values(4,"小李",1);
insert into student(id,user_name,tid) values(5,"小王",2);
- 导入lombok
- 新建实体类
- 建立Mapper接口
- 建立Mapper.xml资源
- 在核心配置文件中绑定注册Mapper
- 测试查询成功
Association
联表查询
select s.id as sid,s.user_name as sname,t.id as tid,t.user_name as tname
from student as s,teacher as t
where s.tid=t.id
接口
List<Student> getStudent2();
接口实现
<!--BFS:联表查询,处理结果--> <select id="getStudent2" resultMap="StudentTeacher2">select s.id as sid,s.user_name as sname,t.id as tid,t.user_name as tnamefrom student as s,teacher as twhere s.tid=t.id </select><resultMap id="StudentTeacher2" type="student"><result property="id" column="sid"/><result property="user_name" column="sname"/><association property="teacher" javaType="teacher"><result property="id" column="tid"/><result property="user_name" column="tname"/></association> </resultMap>
测试
嵌套查询
select *
from student as s
where s.tid
in (select t.id from teacher as t);
定义接口
//查询学生对应的老师信息 List<Student> getStudent();
实现接口
<!--思路:DFS 查询嵌套,1. 查询所有学生信息2. 根据查出来学生的tid,查找对应的teacher --> <select id="getStudent" resultMap="StudentTeacher">select *from mybatis.student; </select><resultMap id="StudentTeacher" type="student"><!--简单属性用 <result property="" column=""/>映射即可--><!--复杂属性多对*————关联association一对*————集合collection--><association property="teacher" column="tid" javaType="teacher" select="getTeacher"/> </resultMap><select id="getTeacher" resultType="teacher">select *from mybatis.teacher where id=#{tid}; </select>
Collection
@Data
public class Student {private Integer id;private String user_name;private Integer tid;
}@Data
public class Teacher {private Integer id;private String user_name;//一个老师拥有多个学生private List<Student> students;
}
实现根据teacher.id查找该老师对应的所有学生
联表查询
select t.id tid,t.user_name tname, s.id sid,s.user_name sname
from teacher t,student s
where t.id=s.tid and t.id=1;
定义接口
//获取某个老师下所有的学生信息 Teacher getTeacherById(@Param("tid") Integer id);
实现接口
<select id="getTeacherById" resultMap="StudentTeacher">select t.id tid,t.user_name tname, s.id sid,s.user_name snamefrom teacher t,student swhere t.id=s.tid and t.id=#{tid}; </select> <resultMap id="StudentTeacher" type="Teacher"><result property="id" column="tid"/><result property="user_name" column="tname"/><!-- 集合中的泛型用ofType指定 --><collection property="students" ofType="student"><result property="id" column="sid"/><result property="user_name" column="sname"/><result property="tid" column="tid"/></collection> </resultMap>
测试
嵌套查询
select tid,(select user_name from teacher where id=1) tname,id sid,user_name sname
from student s
where tid=1;
定义接口
Teacher getTeacherById2(@Param("tid") Integer id);
实现接口
<select id="getTeacherById2" resultMap="StudentTeacher2">select *from mybatis.teacher where id=#{tid} </select> <resultMap id="StudentTeacher2" type="teacher"><collection property="students" column="id" javaType="ArrayList" ofType="student" select="GetStudentByTid" /> </resultMap> <select id="GetStudentByTid" resultType="student">select *from mybatis.studentwhere tid=#{tid}; </select>
测试
动态sql
根据不同的条件生成不同的sql语句
搭建环境
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;
@Data
public class Blog {private String id;private String title;private String author;private Date createTime;private Integer views;
}
@SuppressWarnings("all")//抑制所有警告
public class TestBlog {@Testpublic void test(){SqlSession sqlSession = MyBatisUtils.getSqlSession();BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);Blog blog = new Blog();blog.setId(IDUtil.getId());blog.setAuthor("kuang");blog.setTitle("MyBatis如此简单!");blog.setCreateTime(new Date());blog.setViews(9999);mapper.insert(blog);blog.setId(IDUtil.getId());blog.setTitle("Java如此简单!");blog.setViews(9999);mapper.insert(blog);blog.setId(IDUtil.getId());blog.setTitle("Spring如此简单!");mapper.insert(blog);blog.setId(IDUtil.getId());blog.setTitle("微服务如此简单!");mapper.insert(blog);sqlSession.close();}
}
查询
IF
接口
//查询blog信息List<Blog> queryBlogIF(Map map);
接口实现
<select id="queryBlogIF" resultType="blog" parameterType="map">select *from mybatis.blog where 1=1<if test="title!= null">and titie=#{title}</if><if test="author!= null">and author = #{author}</if> </select>
测试
where优化
<select id="queryBlogIF" resultType="blog" parameterType="map">select *from mybatis.blog<where><if test="title!= null">title=#{title}</if><if test="author!= null">and author = #{author}</if></where>
</select>
- The where element knows to only insert “WHERE” if there is any content returned by the containing tags. (满足条件插入)
- Furthermore, if that content begins with “AND” or “OR”, it knows to strip it off.(保证第一个 where前没有逻辑判断)
choose-when-otherwise&where
switch-case-default | if-else if - else
定义接口
List<Blog> queryBlogIF1(Map map);
实现接口
<select id="queryBlogIF1" parameterType="map" resultType="blog">select *from mybatis.blog<where><choose><when test="id!=null">id=#{id}</when><when test="title != null">title=#{title}</when><when test="author != null">author=#{author}</when><otherwise>views>1000</otherwise></choose></where> </select>
测试
更新
set
- The set element can be used to dynamically include columns to update, and leave out others.(选目标字段,删除无关字符)
- the set element will dynamically prepend the SET keyword,(前置)
- and also eliminate any extraneous commas that might trail the value assignments after the conditions are applied.(删逗号)
定义接口
//更新信息 int updateBlog(Map map);
实现接口
<update id="updateBlog" parameterType="map">update mybatis.blog<set><if test="title!=null">title=#{title},</if><if test="author!=null">author=#{author},</if><if test="views!=null">views=#{views},</if>create_time =#{createTime}</set>where id=#{id} </update>
测试
trim替换
前缀后缀都是 prefix,XXOverrides决定替换的位置
<select id="queryBlogIFByTrim" resultType="blog" parameterType="map">select *from mybatis.blog<trim prefix="where" prefixOverrides="and |or "><if test="title!= null">title=#{title}</if><if test="author!= null">and author = #{author}</if></trim>
</select>
- The prefixOverrides attribute takes a pipe delimited list of text to override, where whitespace is relevant.前缀Overrides 属性采用管道分隔文本列表来重写,其中空白是相关的。(最好写上,替换后可能会出问题)
- The result is the removal of anything specified in the prefixOverrides attribute
- and the insertion of anything in the prefix attribute
<trim prefix="SET" suffixOverrides=",">...
</trim>
sql片段
公共部分抽取出来,方便复用
<sql id=""></sql>
抽取<include refid="" />
引用
<sql id="if-title-author"><if test="title!= null">title=#{title}</if><if test="author!= null">and author = #{author}</if>
</sql><select id="queryBlogIFByTrim" resultType="blog" parameterType="map">select *from mybatis.blog<trim prefix="where" prefixOverrides="and |or "><include refid="if-title-author" /></trim>
</select>
- 基于单表查询
- 不要存在
<where>
标签
foreach
sql
in
的动态范围查询
select *from user where 1=1 and (id=1 or id=2 or id=3);<foreach item="item" collection="ids" open="(" separator=" or " close=")">#{item}
</foreach>
- open:开始符
- separator:分隔符
- close:结束符
- item:项
- colloetion:遍历集合
定义接口
List<Blog> queryBlogIn(Map map);
实现接口
<select id="queryBlogIn" parameterType="map" resultType="blog">select *from mybatis.blog<where><foreach collection="ids" item="id" open="(" separator="or" close=")">id=#{id}</foreach></where> </select>
测试
分页
limit实现分页
sql语句
select *from [table_name] limit [offset],[limit];# [offset]缺省,默认从0开始,到[end]
select *from [table_name] limit [end];
MyBatis方式
接口
//分页查询用户信息 List<User> getUserWithLimit(Map<String,Integer> map);
接口配置
<select id="getUserWithLimit" resultMap="UserMap" resultType="user" parameterType="map">select *from mybatis.user limit #{offset},#{limit}; </select>
测试
@Test public void testGetUserWithLimit(){SqlSession sqlSession = MyBatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);Map<String, Integer> map = new HashMap<String, Integer>();map.put("offset",1);map.put("limit",2);List<User> userList = mapper.getUserWithLimit(map);for (User user : userList) {System.out.println(user);}sqlSession.close(); }
RowBounds实现分页[不建议使用]
不在sql进行分页,通过sqlSession对象实现分页——RowBounds
接口
List<User> getUserWithLimit2();
配置Mapper
<select id="getUserWithLimit2" resultMap="UserMap" resultType="user">select *from mybatis.user; </select>
测试
@Test public void testGetUserWithLimit2(){SqlSession sqlSession = MyBatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);RowBounds rowbounds = new RowBounds(1,2);List<User> userList = sqlSession.selectList("com.kuang.mapper.UserMapper.getUserWithLimit2",null,rowbounds);for (User user : userList) {System.out.println(user);}sqlSession.close(); }
插件——pageHelper
缓存
简介
问题:连接数据库消耗资源
解决:一次查询,保存到高速存储——> 内存
缓存:
- 放在内存中的临时数据
- 将 经常查询且不常改变 的数据存放在缓存,直接从服务器内存取比从服务器磁盘IO速度快,提高查询效率,解决高并发系统的性能问题
使用缓存,减少与数据库交互次数,减少系统开销,提高系统效率
MyBatis缓存
系统默认定义两级缓存:一级缓存和二级缓存
- 一级缓存:SqlSession级,本地缓存
- 二级缓存:手动开启和配置,namespace级缓存
- MyBatis自定义缓存接口Cache,通过实现接口自定义二级缓存
一级缓存——Map
一级缓存默认开启,在一次SESSION期间有效
开启日志
测试一个Session中查询两次相同记录
@Test public void test(){SqlSession sqlSession = MyBatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user1 = mapper.getUserById(1);System.out.println(user1);System.out.println("====================");User user2 = mapper.getUserById(1);System.out.println(user2);System.out.println(user1==user2);sqlSession.close(); }
查看日志输出
两次查找一次查表
两次结果引用同一对象
缓存失效
R不同的东西
CUD必定刷新缓存
不同的Mapper
手动清理缓存
二级缓存
工作机制:
一个Session期间的数据会被放到一级缓存,当Session关闭或提交,对应的一级缓存中的数据被保存到二级缓存中
- 新的Session查询信息,从二级缓存中获取内容
- 不同的mapper查出的数据会放到自己对应的缓存中
mybatis-config.xml 开启二级缓存
<!--开启二级缓存--> <setting name="cacheEnabled" value="true"/>
配置mapper.xml
<!--在当前mapper中使用二级缓存--> <cacheeviction="FIFO"flushInterval="60000"size="512"readOnly="true"/>
- mapper.xml中的所有 select 语句的结果将会被缓存。
- mapper.xml中的所有 insert、update 和 delete 语句会刷新缓存。
- eviction:替换策略
LRU
:默认FIFO
- flushInterval:刷新间隔
- 以毫秒为单位
- 不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新
- size:引用数目
- 默认值是 1024
- readOnly:只读
- 只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改
- 可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false
测试
问题
- 将实体类序列化,否则保错
java.io.NotSerializableException: com.kuang.pojo.User
package com.kuang.pojo;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import java.io.Serializable;@Data @AllArgsConstructor @NoArgsConstructor public class User implements Serializable {private Integer id;private String user_name;private String pwd; }
缓存原理
查找顺序:
- 二级缓存
- 一级缓存
- 数据库
@Test
public void test(){SqlSession sqlSession1 = MyBatisUtils.getSqlSession();User user1 = sqlSession1.getMapper(UserMapper.class).getUserById(1);System.out.println(user1);sqlSession1.close();SqlSession sqlSession2 = MyBatisUtils.getSqlSession();User user2 = sqlSession2.getMapper(UserMapper.class).getUserById(1);System.out.println(user2);sqlSession2.close();System.out.println(user1==user2);SqlSession sqlSession3 = MyBatisUtils.getSqlSession();User user3 = sqlSession3.getMapper(UserMapper.class).getUserById(2);System.out.println(user3);sqlSession3.close();
}
- 由Cache Hit Ratio的计算,可知cache机制是先查二级缓存,再数据库
自定义缓存——Ehcache
开源Java分布式缓存
<dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-ehcache</artifactId><version>1.1.0</version>
</dependency>
<!--mapper.xml-->
<cache type="com.domain.something.MyCustomCache"/>
public interface Cache {String getId();int getSize();void putObject(Object key, Object value);Object getObject(Object key);boolean hasKey(Object key);Object removeObject(Object key);void clear();
}
注解开发
面向接口编程
目的:解耦
接口的理解
- 定义 与 实现 分离
- 接口反映系统设计人员对系统的抽象理解
- 接口分类:
- 一个个体的抽象——抽象体(abstract class)
- 一个个体的某一方面的抽象——抽象面(Interface)
- 接口设计更多体现对系统整体的架构
使用注解开发
本质:反射机制
底层:动态代理
Java Annotations are both limited and messier for more complicated statements.
注解在接口上实现
public interface UserMapper {@Select("select *from user")List<User> getUsers(); }
绑定接口
<!--mybatis-config.xml--> <!-- 绑定接口 --> <mappers><mapper class="com.kuang.mapper.UserMapper"/> </mappers>
测试
@Test public void test(){SqlSession sqlSession = MyBatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> users = mapper.getUsers();for (User user : users) {System.out.println(user);}sqlSession.close(); }
参数
当有多个参数,基本数据类型或String
的参数前加注解 @param
关联参数
引用类型不需要加
sql中使用的是@Param()中设定的属性名
#{} 与 ${} 的区别
- ${}是字符串替换,,Mybatis 在处理${}时,就是把他替换成变量的值
- #{}是预编译处理,会将 #{}替换为?号,调用 PreparedStatement 的 set 方法来赋值;
- 使用#{}可以有效的防止 SQL 注入,提高系统安全性
自动提交事务
public class MyBatisUtils{public static SqlSession getSqlSession(){return sqlSessionFactory.openSession(true);}
}
CRUD
Create
//UserMapper.java
public interface UserMapper{@Insert("insert into user(id,user_name,pwd) values(#{id},#{user_name},#{pwd})")
int insertUser(User user);
}
Update
//UserMapper.java
public interface UserMapper{@Update("update user set user_name=#{user_name},pwd=#{pwd} where id=#{id}")int updateUser(User user);
}
POJO方法的简化——Lombok
IDEA中安装插件
项目中导入依赖
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version><scope>provided</scope> </dependency>
使用注解简化
@Getter and @Setter @ToString @EqualsAndHashCode @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog @Data
@Data,@AllArgsConstructor,@NoArgsConstructor
MybatisPlus
简介
在MyBatis基础上,只做增强不做改变,为简化开发、提高效率而生
- 无侵入
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- CRUD:内置通用 Mapper、通用 Service
- 支持主键自动生成
- 支持 XML 热加载 :Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动
- 支持 ActiveRecord 模式
- 支持自定义全局通用操作:支持全局通用方法注入
- 内置代码生成器 :采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎
- 内置分页插件 、性能分析插件
- 全局拦截(提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作)
- 内置 Sql 注入剥离器:支持 Sql 注入剥离,有效预防 Sql 注入攻击
MtbatisPlus架构
通过简单语句,生成SQL语句,交给MyBatis执行
使用
1. 建库建表
创建数据库 haoke
use haoke;CREATE TABLE `user` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',`name` varchar(30) DEFAULT NULL COMMENT '姓名',`age` int(11) DEFAULT NULL COMMENT '年龄',`email` varchar(50) DEFAULT NULL COMMENT '邮箱',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;-- 插入数据
INSERT INTO `user` (`id`, `name`, `age`, `email`) VALUES ('1', 'Jone', '18','test1@baomidou.com');
INSERT INTO `user` (`id`, `name`, `age`, `email`) VALUES ('2', 'Jack', '20','test2@baomidou.com');
INSERT INTO `user` (`id`, `name`, `age`, `email`) VALUES ('3', 'Tom', '28','test3@baomidou.com');
INSERT INTO `user` (`id`, `name`, `age`, `email`) VALUES ('4', 'Sandy', '21','test4@baomidou.com');
INSERT INTO `user` (`id`, `name`, `age`, `email`) VALUES ('5', 'Billie', '24','test5@baomidou.com');
2. 创建工程及导入依赖
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.3</version>
</parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--mybatis-plus的springboot支持--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version></dependency><!--mysql驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.16</version></dependency>
</dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins>
</build>
3. 编写application.properties文件
spring.application.name = mybatis-plus
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://8.140.130.91:3306/haoke?characterEncoding=utf8&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
4. 创建User对象
package com.mybatisplus.pojo;public class User {private Long id;private String name;private Integer age;private String email;public User() {}public User(Long id, String name, Integer age, String email) {this.id = id;this.name = name;this.age = age;this.email = email;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}
}
5. 编写UserMapper
package com.mybatisplus.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;public interface UserMapper extends BaseMapper<User> {}
6. 编写SpringBoot启动类
package com.mybatisplus;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@MapperScan("com.mybatisplus.mapper") //设置mapper接口的扫描包
@SpringBootApplication
public class Myapplication {public static void main(String[] args) {SpringApplication.run(Myapplication.class,args);}
}
7. 编写SpringBoot启动类
package com.mybatisplus;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@MapperScan("com.mybatisplus.mapper") //设置mapper接口的扫描包
@SpringBootApplication
public class Myapplication {public static void main(String[] args) {SpringApplication.run(Myapplication.class,args);}
}
8. 编写单元测试用例
package com.mybatisplus;import com.mybatisplus.mapper.UserMapper;
import com.mybatisplus.pojo.User;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@SpringBootTest
public class UserMapperTest {@Autowiredprivate UserMapper userMapper;@Testpublic void test(){System.out.println("-------selectAll method test-------");List<User> users = userMapper.selectList(null);for (User user : users) {System.out.println(user);}}
}
通用Mapper
在MybatisPlus中,BaseMapper中定义了一些常用的CRUD方法,当我们自定义的Mapper接口继承BaseMapper后即可拥有了这些方法 【这些方法仅适合单表操作】
/*** Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能*/
public interface BaseMapper<T> extends Mapper<T> {/*** 插入一条记录** @param entity 实体对象*/int insert(T entity);/*** 根据 ID 删除** @param id 主键ID*/int deleteById(Serializable id);/*** 根据 columnMap 条件,删除记录** @param columnMap 表字段 map 对象*/int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);/*** 根据 entity 条件,删除记录** @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)*/int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);/*** 删除(根据ID 批量删除)** @param idList 主键ID列表(不能为 null 以及 empty)*/int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);/*** 根据 ID 修改** @param entity 实体对象*/int updateById(@Param(Constants.ENTITY) T entity);/*** 根据 whereEntity 条件,更新记录** @param entity 实体对象 (set 条件值,可以为 null)* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)*/int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);/*** 根据 ID 查询** @param id 主键ID*/T selectById(Serializable id);/*** 查询(根据ID 批量查询)** @param idList 主键ID列表(不能为 null 以及 empty)*/List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);/*** 查询(根据 columnMap 条件)** @param columnMap 表字段 map 对象*/List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);/*** 根据 entity 条件,查询一条记录** @param queryWrapper 实体对象封装操作类(可以为 null)*/T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);/*** 根据 Wrapper 条件,查询总记录数** @param queryWrapper 实体对象封装操作类(可以为 null)*/Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);/*** 根据 entity 条件,查询全部记录** @param queryWrapper 实体对象封装操作类(可以为 null)*/List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);/*** 根据 Wrapper 条件,查询全部记录** @param queryWrapper 实体对象封装操作类(可以为 null)*/List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);/*** 根据 Wrapper 条件,查询全部记录* <p>注意: 只返回第一个字段的值</p>** @param queryWrapper 实体对象封装操作类(可以为 null)*/List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);/*** 根据 entity 条件,查询全部记录(并翻页)** @param page 分页查询条件(可以为 RowBounds.DEFAULT)* @param queryWrapper 实体对象封装操作类(可以为 null)*/<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);/*** 根据 Wrapper 条件,查询全部记录(并翻页)** @param page 分页查询条件* @param queryWrapper 实体对象封装操作类*/<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}
通过id查询——selectById
@Test
public void testSelectById(){System.out.println("通过Id查询");User user = userMapper.selectById(3L);//数据类型为Long,id为3System.out.println(user);
}
模糊查询——like
条件查询
https://mp.baomidou.com/guide/wrapper.html#abstractwrapper
插入数据
@Test
public void testSave(){User user = new User();user.setAge(25);user.setEmail("zhangsan@qq.com");user.setName("zhangsan");int count = userMapper.insert(user);System.out.println("新增数据成功! count=>"+count);
}
id自增问题
所以自增问题出现在java参数传递中
public class User {@TableId(value = "ID", type = IdType.AUTO)private Long id;private String name;private Integer age;private String email;
}
删除数据
修改数据
根据id修改,只修改指定的字段
分页查询
/*** 分页插件*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false// paginationInterceptor.setOverflow(false);// 设置最大单页限制数量,默认 500 条,-1 不受限制// paginationInterceptor.setLimit(500);PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();paginationInnerInterceptor.setDbType(DbType.MYSQL);interceptor.addInnerInterceptor(paginationInnerInterceptor);return interceptor;
}
配置
使用MyBatis原生配置文件
# 指定全局配置文件
mybatis-plus.config-location = classpath:mybatis-config.xml
# 指定mapper.xml文件
mybatis-plus.mapper-locations = classpath*:mybatis/*.xml
若指定配置文件,无需配置环境,只需要
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- configuration的核心配置 -->
<configuration><!-- 每一个Mapper.xml都需要在MyBatis核心配置文件中注册 -->
</configuration>
https://mp.baomidou.com/guide/config.html#%E5%9F%BA%E6%9C%AC%E9%85%8D%E7%BD%AE
Lombok
- @Data:注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、toString 方法
- @Setter:注解在属性上;为属性提供 setting 方法
- @Getter:注解在属性上;为属性提供 getting 方法
- @Slf4j:注解在类上;为类提供一个 属性名为log 的 slf4j日志对象
- @NoArgsConstructor:注解在类上;为类提供一个无参的构造方法
- @AllArgsConstructor :注解在类上;为类提供一个全参的构造方法
- @Builder :使用Builder模式构建对象
【开发】后端框架——Mybatis相关推荐
- java开发后端框架_java开发后端框架
1 java开发后端框架 java从推出到现在技术不断发展,语言也优化的越来越好,对于java工程师来说技术的不断发展,他们需要不断学习java进阶,而对于新手来说就能从基础到核心.那么新手该怎么学习 ...
- 【基于Springboot、Mybatis后端框架开发——招生管理网站(系统)】附源码
招生管理网站(系统) 招生管理系统项目简介 内容及步骤 1. 招生管理系统的总体设计与实现 1.1需求分析 1.2系统功能设计 1.4系统功能测试(部分) 1.5代码下载 招生管理系统项目简介 搭建J ...
- 一文搞定7大流行后端框架:Spring、Netty、MyBatis、Hibernate、Dubbo...
框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法:另一种定义认为,框架是可被应用开发者定制的应用骨架.前者是从应用方面而后者是从目的方面给出的定义. 可 ...
- mysql后台框架_后端服务开发 - 认识后端框架
后端的基本概念 在软件架构和程序设计领域,前端是软件系统中直接和用户交互的部分,而后端控制着软件的输出. 前端控制ajax等技术向后端进行网络请求:后端收到请求后对数据库进行操作,返回给前端JSON数 ...
- 后端开发知识框架汇总
后端开发知识框架汇总 Spring框架 Spring/Springboot/SpringMVC Spring 其是一个引擎,众多衍生产品例如boot.security.jpa等等:但他们的基础都是 ...
- 跟我学Springboot开发后端管理系统6:缓存框架Caffeine
Caffeine是一个基于Java8的高性能缓存框架,号称趋于完美.Caffeine受启发于Guava Cache的API,使用API和Guava是一致的.它借鉴了Guava Cache和Concur ...
- Java软件开发:自定义MyBatis持久层框架
自定义MyBatis持久层框架 1 框架概述 1.1 什么是框架 1.2 框架要解决的问题 1.3 软件开发的分层的重要性 2 MyBatis框架 3 JDBC编程 3.1 JDBC程序的回顾 3.2 ...
- python后端框架flask_Vue+Flask轻量级前端、后端框架,如何完美同步开发
导言我们的Vue2.0应用:简读-微信公众号RSS,即将进入后端开发. Vue+Flask作为轻量级前端.后端框架,非常适合组合起来快速开发.一个是js,一个是Python. Bonus: 可以完美实 ...
- python web后端和vue哪个难_全栈开发用纯后端模板与Vue+后端框架组合哪个好?
全栈开发没有明确的定义,但应该指的就是前端+后端+数据库.所以只用纯后端框架,不算全站开发.至少在Angularjs出现以前,我没听说过全站开发这个词. 你问题描述中的感觉是对的,这就是前后端分离的好 ...
最新文章
- 【Java挠头】Java异常、捕获、处理、throw、throws等绝妙剖析
- Python之一行代码
- 【Python】Effective Python 读书笔记
- [python爬虫] Selenium常见元素定位方法和操作的学习介绍(转载)
- Boost库之function的使用
- 蓝桥杯第六届省赛JAVA真题----生命之树
- 网站被DDOS***怎么防御,推荐网站CDN防御
- Python十行代码带你穿越管世界
- 疫情期间我们与你同在:关爱障碍群体刚需,坚持做无障碍的倡议书
- [转载] numpy.reshape用法(自用)
- SVN安装-配置-使用及myeclipse的插件安装(图文)
- 安装HDFS过程中Browse Directory报错
- 全网首发:JDK绘制文字:四、绘制文字的具体函数分析
- 苯酚吸附专用树脂 污水中的苯酚怎么去除
- jquery实现类似以前凡客诚品右侧图文切换结合效果
- 【JPress】jpress-core架构
- 【金猿技术展】同盾科技知识联邦技术——3.0人工智能的坚强基石
- 科技巨头纷纷发力AI,智能硬件已来临,变现还会远吗?
- 如何在Windows 10 IoT Core中添加其他语言的支持,如中文
- 软件测试工程师项目业绩怎么写,软件工程师的“项目业绩”如何才能脱颖而出?...
热门文章
- Java 扫描微信公众号二维码,关注并自动登录网站
- BAPI_PRODORDCONF_GET_TT_PROP 生产订单完工确认
- C#基础+面向对象学习
- Vue运行项目常用命令
- 免费开源好用还佛系的国产PDF软件:pdf补丁丁下载 | 含pdf补丁丁使用手册
- fedroa设置启动快捷键
- A Benchmark and Simulator for UAV Tracking(论文翻译)
- 华为HCIA鲲鹏生态体系介绍
- UVA10815 安迪的第一个字典 Andy‘s First Dictionary
- 字体的分类图示——对网页设计很有益的图