mybatis简介
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)
基本信息
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录
背景介绍
MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Ordinary Java Objects,普通的 Java对象)映射成数据库中的记录。每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。SqlSessionFactoryBuilder可以从一个xml配置文件或者一个预定义的配置类的实例获得。用xml文件构建SqlSessionFactory实例是非常简单的事情。推荐在这个配置中使用类路径资源(classpath resource),但你可以使用任何Reader实例,包括用文件路径或file://开头的url创建的实例。MyBatis有一个实用类----Resources,它有很多方法,可以方便地从类路径及其它位置加载资源。
特点
简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。提供映射标签,支持对象与数据库的orm字段关系映射提供对象关系映射标签,支持对象关系组建维护提供xml标签,支持编写动态sql。
创建第一个mybatis项目
导入依赖 pom.xml
<dependencies>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
编写配置文件 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>
<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://192.168.42.52:3306/myDatabase?useSSL=true&useUnicode=true&charactorEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/kuang/dao/UserMapper.xml"/>
</mappers>
</configuration>
编写实体类
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 + '\'' +
'}';
}
}
编写接口类
public interface UserDao {
List<User> getUserList();
}
编写mapper
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace绑定的类相当于实现了这个接口-->
<mapper namespace="com.kuang.dao.UserDao">
<select id="getUserList" resultType="com.kuang.pojo.User">
select * from myDatabase.myuser
</select>
</mapper>
database工具文件 MybatisUtils.java
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
String resource = "mybatis-config.xml";
try {
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/***
* 获取sqlSession
* @return
*/
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
测试
@Test
public void test01(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
报错解决
Caused by: org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### The error may exist in com/kuang/dao/UserMapper.xml
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource com/kuang/dao/UserMapper.xml
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:80)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:64)
at com.kuang.utils.MybatisUtils.<clinit>(MybatisUtils.java:16)
... 26 more
错误原因:
使用Maven构建项目的时候,会默认过滤掉静态资源,所以,需要手动来配置
解决方法;pom.xml中添加如下配置
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
CRUD 实现增删改查
public interface UserMapper {
List<User> getUserList();
User getUserById(Integer id);
Integer addUser(User user);
Integer updateUser(User user);
Integer deleteUser(Integer id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace绑定的类相当于实现了这个接口-->
<mapper namespace="com.kuang.dao.UserMapper">
<select id="getUserList" resultType="com.kuang.pojo.User">
select * from myDatabase.myuser
</select>
<select id="getUserById" resultType="com.kuang.pojo.User" parameterType="Integer">
select * from myDatabase.myuser where id=#{id}
</select>
<insert id="addUser" parameterType="com.kuang.pojo.User">
INSERT INTO myDatabase.myuser(id,name,pwd) VALUES (#{id},#{name},#{pwd})
</insert>
<update id="updateUser" parameterType="com.kuang.pojo.User">
UPDATE myDatabase.myuser SET name=#{name},pwd=#{pwd} WHERE id=#{id}
</update>
<delete id="deleteUser" parameterType="Integer">
DELETE from myDatabase.myuser WHERE id=#{id}
</delete>
</mapper>
测试
public class MyTest {
@Test
public void test01(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
List<User> userList = userDao.getUserList();
// List<User> userList = sqlSession.selectList("com.kuang.dao.UserDao.getUserList");
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
@Test
public void test02(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper sessionMapper = sqlSession.getMapper(UserMapper.class);
User user = sessionMapper.getUserById(1);
System.out.println(user);
sqlSession.close();
}
@Test
public void test03(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper sessionMapper = sqlSession.getMapper(UserMapper.class);
Integer user = sessionMapper.addUser(new User(4, "hahah", "123444"));
if(user>0){
System.out.println("增加成功");
}
// 增删改要提交事务别忘记
sqlSession.commit();
sqlSession.close();
}
@Test
public void test04(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper sessionMapper = sqlSession.getMapper(UserMapper.class);
Integer user = sessionMapper.updateUser(new User(4, "hehe", "ttttt"));
if(user>0){
System.out.println("修改成功");
}
// 增删改要提交事务别忘记
sqlSession.commit();
sqlSession.close();
}
@Test
public void test05(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper sessionMapper = sqlSession.getMapper(UserMapper.class);
Integer user = sessionMapper.deleteUser(4);
if(user>0){
System.out.println("删除成功");
}
// 增删改要提交事务别忘记
sqlSession.commit();
sqlSession.close();
}
}
map的使用
User getUserById2(Map<String,Object> map);
Integer addUser2(Map<String,Object> map);
<select id="getUserById2" resultType="com.kuang.pojo.User" parameterType="map">
select * from myDatabase.myuser where id=#{id} and name=#{name}
</select>
<insert id="addUser2" parameterType="map">
INSERT INTO myDatabase.myuser(id,name,pwd) VALUES (#{userid},#{username},#{userpwd})
</insert>
@Test
public void test021(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper sessionMapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map=new HashMap<String,Object>();
map.put("id",6);
map.put("name","ceshi");
User user = sessionMapper.getUserById2(map);
System.out.println(user);
sqlSession.close();
}
模糊查询
<select id="getUserById2" resultType="com.kuang.pojo.User" parameterType="map">
select * from myDatabase.myuser where name LIKE "%"#{name}"%"
</select>
属性配置
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
注意这几个是有顺序要求的。
configuration的使用
db.properties 文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.42.52:3306/myDatabase?useSSL=true&useUnicode=true&charactorEncoding=UTF-8
username=root
password=123456
<!--引入外部配置文件-->
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
username和password 在两边都配置,默认使用外部配置文件的
typeAliases的使用
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写
mybatis-config.xml 配置文件
<typeAliases>
<typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>
mapper配置文件
<select id="getUserList" resultType="User">
select * from myDatabase.myuser
</select>
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。
@Alias("author")
public class Author {
}
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator
设置(settings)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等
几个重要的参数
设置名 描述 有效值 默认值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
azyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false
useGeneratedKeys 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 true | false False
mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 true | false False
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置
完整配置
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
mapper
方式一;
<mappers>
<mapper resource="com/kuang/dao/UserMapper.xml"/>
</mappers>
方式二:
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
第二种方式注意点:
1.接口和它对应的mapper必须同名
2.接口和它对应的mapper必须在同一个文件夹下
方式三
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
第三种方式注意点:
1.接口和它对应的mapper必须同名
2.接口和它对应的mapper必须在同一个文件夹下
推荐使用方式一
作用域(Scope)和生命周期
理解我们之前讨论过的不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题
SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。
SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
SqlSession
每个线程都应该有它自己的 SqlSession 实例。
SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。使用完必须关闭。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}
结果集映射,解决列名和实体类不匹配的问题
使用resultMap 解决
UserMapper.xml 文件修改如下:
<resultMap id="userResultMap" type="User">
<!--column :数据库中的列 property:实体类中的列-->
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="getUserById" resultMap="userResultMap">
select * from myDatabase.myuser where id=#{id}
</select>
日志
标准日志
<setting name="logImpl" value="STDOUT_LOGGING"/>
可以直接使用
log4j
log4介绍
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
使用log4j
导入maven依赖
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
设置settings
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
log4j.properties
#rootLogger的配置方式:
log4j.rootLogger=DEBUG,console,file
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#控制台输出:
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern =[%p][%d{yy-MM-dd}][%c]%m%n
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
log4j简单使用
static Logger logger= Logger.getLogger(MyTest.class);
分页
limit 分页
SELECT * FROM myuser LIMIT 0,2
List<User> getUserByLimit(Map<String,Object> map);
<select id="getUserByLimit" parameterType="map" resultMap="userResultMap">
SELECT * FROM myDatabase.myuser limit #{start},#{pageSize}
</select>
@Test
public void test03(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("start",2);
hashMap.put("pageSize",2);
List<User> user = mapper.getUserByLimit(hashMap);
for (User user1 : user) {
System.out.println(user1);
}
sqlSession.close();
}
RowBounds分页
List<User> getUserByRowBounds();
<select id="getUserByRowBounds" resultMap="userResultMap">
SELECT * FROM myDatabase.myuser
</select>
@Test
public void test03(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
RowBounds rowBounds = new RowBounds(0, 1);
List<User> list = sqlSession.selectList("com.kuang.dao.UserMapper.getUserByRowBounds",null,rowBounds);
for (User user1 : list) {
System.out.println(user1);
}
sqlSession.close();
}
分页插件 PageHelper
注解
public interface UserMapper {
@Select("select * from myuser")
List<User> getUsers();
}
<mappers>
<mapper class="com.kuang.dao.UserMapper"/>
</mappers>
@Test
public void test03(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
for (User user : mapper.getUsers()) {
System.out.println(user);
}
sqlSession.close();
}
注解实现CRUD
自动提交事务
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
注解的使用
// @Param的参数对应sql中的参数
@Select("select * from myuser where id=#{qid}")
User getUserById(@Param("qid") int id);
@Insert("insert into myuser(id,name,pwd) values(#{id},#{name},#{password})")
int addUser(User user);
注意@Param(“qid”)的使用
1.基本类型或者String 类型需要加上
2.引用类型不需要加
3.只有一个基本类型的参数可以不用加
4.在sql中使用的是@Param中设置的参数名
sql中取参数的方法,#{} 和${} 的区别
#{}:使用的是预编译,对应JBDC中的PreparedStatement
${}:mybatis不会修改或者转义字符换,直接输出变量值
为什么大多数的时候,我们使用的是#{}?
这里就要提到SQL注入的问题,#{}是以参数的形式,实际的SQL语句为参数值加单引号 ,从而避免了SQL注入的问题。
而${}直接输出参数值,mybatis不做转义,可能引发SQL注入问题。
测试
@Test
public void test03(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.addUser(new User(4,"Hello","tttt"));
// User id = mapper.getUserById(1);
// System.out.println(id);
sqlSession.close();
}
lombok
1。安装插件
2.导入依赖
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
注解如下
@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
@val
@var
experimental @var
@UtilityClass
尽量别用
多对一处理
第一种方式:子查询
public interface StudentMapper {
List<Student> getStudent();
}
<select id="getStudent" resultMap="StudentTeacher">
SELECT * FROM myDatabase.student
</select>
<resultMap id="StudentTeacher" type="Student">
<result column="id" property="id"/>
<result column="name" property="name"/>
<!--复杂属性
对象association
集合collection
-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
<!--<collection property=""/>-->
</resultMap>
<select id="getTeacher" resultType="Teacher">
SELECT * FROM myDatabase.teacher WHERE id=#{tid};
</select>
测试
@Test
public void test02(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.getStudent();
for (Student student : students) {
System.out.println(student);
}
sqlSession.close();
}
第二种方式 按照结果嵌套 联表查询
List<Student> getStudent2();
<select id="getStudent2" resultMap="StudentTeacher2">
SELECT s.id sid,s.name sname,t.name tname FROM myDatabase.student s,myDatabase.teacher t where s.tid=t.id
</select>
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
推荐第二种
一对多处理
第一种方式:嵌套处理 推荐
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Teacher {
private int id;
private String name;
private List<Student> students;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
private int id;
private String name;
private int tid;
// private Teacher teacher;
}
TeacherMapper.java
// 获取指定老师下的所有学生及老师信息
Teacher getStudentByTid(@Param("tid") int id);
TeacherMapper.xml
<select id="getStudentByTid" resultMap="TeStu">
SELECT s.id sid,s.name sname,t.id tid,t.name tname FROM myDatabase.teacher t,myDatabase.student s where s.tid=t.id and t.id=#{tid}
</select>
<resultMap id="TeStu" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!--
javaType 指定属性的类型
ofType 指定泛型信息
-->
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
第二种方式
<select id="getStudentByTid2" resultMap="TeStu2">
SELECT * FROM myDatabase.teacher WHERE id=#{tid}
</select>
<resultMap id="TeStu2" type="Teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
SELECT *FROM myDatabase.student WHERE tid=#{tid}
</select>
小结
1.关联 :association
2.集合:collection
3.javaType & ofType
javaType 指定实体类中属性的类型
ofType 指定集合中的pojo类型,即泛型类型
动态sql
搭建环境
实体类
@Data
@ToString
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}
mapper类
public interface BlogMapper {
int addBolg(Blog blog);
List<Blog> getBlogIF(Map map);
}
mapper.xml 文件
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kuang.dao.BlogMapper">
<insert id="addBolg" parameterType="blog">
INSERT INTO myDatabase.blog(id,title,author,create_time,views)
VALUES (#{id},#{title},#{author},#{createTime},#{views})
</insert>
<select id="getBlogIF" parameterType="map" resultType="blog">
SELECT * FROM myDatabase.blog WHERE 1=1
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</select>
</mapper>
测试类
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IDUtils.getId());
blog.setTitle("ceshi2");
blog.setAuthor("kuangshen2");
blog.setCreateTime(new Date());
blog.setViews(222);
mapper.addBolg(blog);
sqlSession.commit();
sqlSession.close();
}
@Test
public void test02(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap<Object, Object> map = new HashMap<>();
map.put("author","kuangshen");
List<Blog> blogs = mapper.getBlogIF(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
}
if
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
如果不传入 “title”,那么所有处于 “ACTIVE” 状态的 BLOG 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果
choose、when、otherwise
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG
###trim、where
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
上述例子有个问题,可能会出现
SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’
这个查询也会失败。这个问题不能简单地用条件元素来解决
MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
prefix:前缀
prefixoverride:去掉第一个and或者是or
移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容
set
用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列
<update id="updateBlog" parameterType="map">
UPDATE myDatabase.blog
<set>
<if test="title != null">
title=#{title},
</if>
<if test="author != null">
author=#{author},
</if>
</set>
WHERE id=#{id}
</update>
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)
include
抽取片段
<sql id="if-title-author">
<choose>
<when test="title != null">
title=#{title}
</when>
<when test="author != null">
and author=#{author}
</when>
<otherwise>
<if test="views!=null">
and views=#{views}
</if>
</otherwise>
</choose>
</sql>
引用
<select id="getBlogIF" parameterType="map" resultType="blog">
SELECT * FROM myDatabase.blog
<where>
<include refid="if-title-author"></include>
</where>
</select>
注意事项:最好基于单表查询抽取片段
foreach
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!
缓存【cache】
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
基本上就是这样。这个简单语句的效果如下:
映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
缓存不会定时进行刷新(也就是说,没有刷新间隔)。
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:
LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU。
一级缓存
缓存失效的情况:
1.查询不同的东西
2.增删改操作可能会改变原来的数据,所以必定会刷新缓存
3.查询不同的mapper.xml
4.手动清理缓存
sqlSession.clearCache();
一级缓存m默认开启,只在一个sqlSession中有效,从开启到关闭这一个区间段
二级缓存
二级缓存基于namespace级别的缓存,一个名称空间对应一个缓存
<!--开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
<!--在当前命名空间中开启缓存-->
<cache/>
<!--全局开启二级缓存-->
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
<!--useCache:单个设置缓存-->
<select id="getUserById" parameterType="_int" resultType="user" useCache="true">
SELECT * FROM myDatabase.myuser WHERE id=#{id}
</select>
注意:只用 时要将实体类序列化,否则会报错。
小结只要开启了二级缓存,在同一个mapper下就有效。
所有的数据都会先放在一级缓存中,只有当一级缓存关闭后才会提交到二级缓存中。