Mybatis
Mybatis介绍
什么是Mybatis?
mybatis是支持普通SQL查询、存储过程和高级映射的优秀持久层框架。
Mybatis优点
- 几乎消除了JDBC代码和参数的手动设置
- 消除结果集的检索
- 使用XML或注解用于配置和原始映射,将接口和POJOs(实体类)映射成数据库中的记录。
Mybatis特点
- 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql
Mybatis功能构架
我们把Mybatis的功能架构分为三层:
1.API接口层 2.数据处理层 3.基础支撑层
**API接口:**提供给外部使用的API接口,开发人员使用本地API接口操作数据库。接口层一收到调用请求,就会调用数据处理层进行具体的数据处理。
**数据处理层:**负责具体的SQL查询,SQL解析、SQL执行以及执行结果集映射的处理。主要目的是根据调用进行一次操作数据库
**基础支撑层:**负责最基础的支撑,包括 连接管理、事务管理、配置加载、缓冲加载。这些都是公用的的基础组件。
框架解析
框架架构讲解:
mybatis结构
(1)加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
(2)SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。
(3)SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。
(4)结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,并将最终结果返回。
获取Mybatis
- maven
- GitHub
持久层
持久化
- 将程序的数据在持久状态和瞬时状态转化的过程。
- 内存的特点:断电即失
第一个Mybatis程序
搭建环境
1.搭建数据库
CREATE DATABASS mybatis;
创建表
USE mybatis | |
go | |
CREATE TABLE Users( | |
id int not null PRIMARY KEY, | |
name varchar default NULL, | |
pwd varchar default NULL | |
) |
插入值
insert Users(id,name,pwd) | |
values (1,'张三','123456') | |
insert Users(id,name,pwd) | |
values (2,'李四','123456') | |
insert Users(id,name,pwd) | |
values (3,'王五','123456') |
另外一种值插入方法
insert into Users | |
select 1,'张三','123456' | |
union |
2.创建项目
导入依赖
sqlServer驱动
<!--sqlServer驱动--> | |
<dependency> | |
<groupId>com.microsoft.sqlserver</groupId> | |
<artifactId>mssql-jdbc</artifactId> | |
<version>8.2.2.jre8</version> | |
</dependency> |
mybatis
<!--mybatis--> | |
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> | |
<dependency> | |
<groupId>org.mybatis</groupId> | |
<artifactId>mybatis</artifactId> | |
<version>3.5.5</version> | |
</dependency> |
junit
<!--junit--> | |
<dependency> | |
<groupId>junit</groupId> | |
<artifactId>junit</artifactId> | |
<version>4.12</version> | |
<scope>test</scope> | |
</dependency> |
创建Module 作为子模块
编写代码
编写Mybatis核心配置文件
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.microsoft.sqlserver.jdbc.SQLServerDriver"/> | |
<property name="url" value="jdbc:sqlserver://localhost:1433;database=mybatis;useSSL=true&useUnicode=true&characterEncoding=utf-8"/> | |
<property name="username" value="sa"/> | |
<property name="password" value="1582183834"/> | |
</dataSource> | |
</environment> | |
</environments> | |
<mappers> | |
<mapper resource="com/Dao/UsersMapper.xml"/> | |
</mappers> | |
</configuration> |
编写工具类
编写工具类 加载使用
public class UsersUtils { | |
private static SqlSessionFactory sqlSessionFactory; | |
static{ | |
//获取sqlSessionFactory对象 | |
try { | |
String resource ="mybatis-config.xml"; | |
InputStream resourceAsStream = Resources.getResourceAsStream(resource); | |
sqlSessionFactory =new SqlSessionFactoryBuilder().build(resourceAsStream); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
//获取SqlSession实例 | |
//既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。 | |
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。 | |
public static SqlSession getSqlSession(){ | |
return sqlSessionFactory.openSession(); | |
// SqlSession sqlSession = sqlSessionFactory.openSession(); | |
// return sqlSession; | |
} | |
} |
编写实体类
public class Users implements Serializable { | |
private int id; | |
private String name; | |
private String pwd; | |
public Users() { | |
} | |
public Users(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; | |
} | |
public String toString() { | |
return "Users{" + | |
"id=" + id + | |
", name='" + name + '\'' + | |
", pwd='" + pwd + '\'' + | |
'}'; | |
} | |
} |
编写Dao接口 层
interface UsersDao { | |
List<Users> getUsersDaoList(); | |
} |
编写Dao接口层实现代码的xml文件
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |
<mapper namespace="com.Dao.UsersDao"> | |
<select id="getUsersDaoList" resultType="com.pojo.Users"> | |
select * from Users | |
</select> | |
</mapper> |
namespace=“com.Dao.UsersDao”
全限命名法,namespace 表示和某一个类或接口 相匹配
id=“getUsersDaoList”
Select 中的ID表示查询的方法
resultType=“com.pojo.Users”
返回单个的类型,必须表示返回类型所对应的实体类。
测试
测试注意
一般在Text下创建和代码相对应的目录进行测试。
编写测试代码
public class Text { | |
@Test | |
public void text(){ | |
//加载resources资源 | |
//获取SqlSession对象 | |
SqlSession sqlSession = UsersUtils.getSqlSession(); | |
//执行SQL | |
UsersDao mapper = sqlSession.getMapper(UsersDao.class); | |
List<Users> usersDao = mapper.getUsersDaoList(); | |
for (Users users : usersDao) { | |
System.out.println(users); | |
} | |
//关闭资源 | |
sqlSession.close(); | |
} | |
} |
测试果
可能会出现的错误
org.apache.ibatis.binding.BindingException: Type interface com.Dao.UsersDao is not known to the MapperRegistry.
错误原因
在核心配置文件中没有没有将UsersDao的xml文件配置注册
解决
<mappers> | |
<mapper resource="com/Dao/UsersMapper.xml"/> | |
</mappers> |
将UsersDao的xml文件配置到Mybatis核心配置文件当中
IOException: Could not find resource com/Dao/UsersMapper.xml
错误原因
1.没有编写UsersMapper.xml配置文件。
2.因为在Maven项目中,约定大于配置,所以需要手动将Java中的xml打包带出
解决
1.编写UsersMapper.xml文件
2.在Pom文件下加上Build
<build> | |
<resources> | |
<resource> | |
<directory>src/main/resources</directory> | |
<includes> | |
<include>**/*.properties</include> | |
<include>**/*.xml</include> | |
</includes> | |
<filtering>true</filtering> | |
</resource> | |
<resource> | |
<directory>src/main/java</directory> | |
<includes> | |
<include>**/*.properties</include> | |
<include>**/*.xml</include> | |
</includes> | |
<filtering>true</filtering> | |
</resource> | |
</resources> | |
</build> |
项目代码结构
CURD
查询
UsersDao接口
//通过id查询用户 | |
Users getUsersById(int id); |
映射文件
<select id="getUsersById" resultType="com.pojo.Users" parameterType="int"> | |
select * from Users where id= | |
</select> |
测试结果
public void Text01(){ | |
SqlSession sqlSession = UsersUtils.getSqlSession(); | |
UsersDao mapper = sqlSession.getMapper(UsersDao.class); | |
Users usersById = mapper.getUsersById(1); | |
System.out.println(usersById); | |
sqlSession.close(); | |
} |
增加
UsersDao接口
//增加用户 | |
int InsertUser(Users users); |
映射文件
<insert id="InsertUser" parameterType="com.pojo.Users"> | |
insert Users(id,name,pwd) values(#{id},#{name},#{pwd}) | |
</insert> |
测试
//增加用户 | |
@Test | |
public void Text02(){ | |
SqlSession sqlSession = UsersUtils.getSqlSession(); | |
UsersDao mapper = sqlSession.getMapper(UsersDao.class); | |
int s = mapper.InsertUser(new Users(4, "五百", "123456")); | |
if (s>0){ | |
System.out.println("增加用户成功"); | |
} | |
//增删改需要提交事务,否则无法更改 | |
sqlSession.commit(); | |
sqlSession.close(); | |
} |
修改
UsersDao接口
//更改用户数据 | |
int UpdataUser(Users users); |
映射文件
<update id="UpdataUser" parameterType="com.pojo.Users"> | |
update Users set name=#{name},pwd=#{pwd} where id=#{id}; | |
</update> |
测试
//修改用户 将四号用户的名字改为六百 | |
@Test | |
public void text03(){ | |
SqlSession sqlSession = UsersUtils.getSqlSession(); | |
UsersDao mapper = sqlSession.getMapper(UsersDao.class); | |
int liu = mapper.UpdataUser(new Users(4, "六百", "123456")); | |
if (liu>0){ | |
System.out.println("修改用户成功"); | |
} | |
//提交事务 | |
sqlSession.commit(); | |
sqlSession.close(); | |
} |
删除
UsersDao接口
//删除用户数据 | |
int DelectUser(int id); |
映射文件
<delete id="DelectUser" parameterType="int"> | |
delete from Users where id=#{id}; | |
</delete> |
测试
@Test | |
public void text04(){ | |
SqlSession sqlSession = UsersUtils.getSqlSession(); | |
UsersDao mapper = sqlSession.getMapper(UsersDao.class); | |
int i = mapper.DelectUser(4); | |
if (i>0){ | |
System.out.println("删除用户成功"); | |
} | |
sqlSession.commit(); | |
sqlSession.close(); | |
} |
完整代码
UsersDao接口
public interface UsersDao { | |
//获取列表 | |
List<Users> getUsersDaoList(); | |
//通过id查询用户 | |
Users getUsersById(int id); | |
//增加用户 | |
int InsertUser(Users users); | |
//更改用户数据 | |
int UpdataUser(Users users); | |
//删除用户数据 | |
int DelectUser(int id); | |
} |
映射文件
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |
<mapper namespace="com.Dao.UsersDao"> | |
<select id="getUsersDaoList" resultType="com.pojo.Users"> | |
select * from Users | |
</select> | |
<select id="getUsersById" resultType="com.pojo.Users" parameterType="int"> | |
select * from Users where id=#{id} | |
</select> | |
<insert id="InsertUser" parameterType="com.pojo.Users"> | |
insert Users(id,name,pwd) values(#{id},#{name},#{pwd}) | |
</insert> | |
<update id="UpdataUser" parameterType="com.pojo.Users"> | |
update Users set name=#{name},pwd=#{pwd} where id=#{id}; | |
</update> | |
<delete id="DelectUser" parameterType="int"> | |
delete from Users where id=#{id}; | |
</delete> | |
</mapper> |
XML配置
首先声明xml中各个配置设置的位置是固定不变的,否则会出现异常
必须遵循下面的顺序进行设置。
(properties–settings–typeAliases–typeHandlers–objectFactory–objectWrapperFactory–reflectorFactory–plugins–environments–databaseIdProvider–mappers)"
属性(properties)
目前我们学过的属性文件db.properties
在xml文件当中,为了实现我们可以动态修改xml文件,可以在外部设置属性文件引用到mybatis-config.xml文件当中。
属性配置有两种插入方法
1.内部引入
<properties > | |
<property name="username" value="sa"/> | |
<property name="password" value="1582183834"/> | |
</properties> |
2.外部引入
2.1 首先编写一个properties属性文件。
driver=com.microsoft.sqlserver.jdbc.SQLServerDriver | |
url=jdbc:sqlserver://localhost:1433;database=mybatis;useSSL=true&useUnicode=true&characterEncoding=UTF-8 | |
username=sa | |
password=1582183834 |
然后在mybatis-config.xml文件当中
<properties resource="db.properties"></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> |
通过${}以实现动态获取properties属性文件当中的属性
若两部分都有部署,首先会执行内部的xml,再执行外部的属性资源,但是内部的某些属性会被外部属性覆盖!
类型别名(typeAliases)
类型别名相当于对动态SQL中的返回类型或者参数类型的优化
例如在查询表单的SQL中的返回类型resultType的返回类型是一个对象
<select id="getUsersDaoList" resultType="com.pojo.Users"> | |
select * from Users | |
</select> |
这样些写较为麻烦,所以为这个类型设置一个别名
<typeAliases> | |
<typeAlias type="com.pojo.Users" alias="Users"/> | |
</typeAliases> |
这样设置类型为Users类的别名就变为Users
<select id="getUsersDaoList" resultType="Users"> | |
select * from Users | |
</select> |
package类型别名
package 顾名思义是包 package类型别名,即是扫描包
<typeAliases> | |
<package name="com.pojo"/> | |
</typeAliases> |
扫描包
每一个在包 domain.blog
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
例如 在pojo包下有一个名为User的类,那么扫描后的返回类型就为user
注解别名
和上侧相同,当包下的类有注解时,注解为Alias(“”)
则此时的返回类型就为注解内容,并且和类名无关
例如 在pojo包下有一个名为User的类,注解为Alias(“hello”),那么扫描后的返回类型为hello,并不为user
换一种说法,也就是在扫描包别名是,注解的优先级大于类名别名优先级
映射器配置
映射器
映射器配置共有3中配置方法
1.resource
<mappers> | |
<mapper resource="com/Dao/UsersMapper.xml"/> | |
</mappers> |
配置这种资源,每一层需要斜杠间隔(/)
2.class
<mappers> | |
<mapper class="com.Dao.UsersMapper"/> | |
</mappers> |
使用映射器中的class映射必须遵守以下规则,否则就会报错
- 接口和它的Mapper配置文件名称保持一致
- 接口和它的Mapper配置文件需要在同一个包下
3.package扫描方法
<mappers> | |
<package name="com.Dao"/> | |
</mappers> |
使用映射器中的package方法也必须遵循以下规则
- 接口和它的Mapper配置文件名称保持一致
- 接口和它的Mapper配置文件需要在同一个包下
另外还有一种url路径反射,因为路径问题,所以很难寻找(不建议使用)。
<!-- 使用完全限定资源定位符(URL) --> | |
<mappers> | |
<mapper url="file:///var/mappers/AuthorMapper.xml"/> | |
<mapper url="file:///var/mappers/BlogMapper.xml"/> | |
<mapper url="file:///var/mappers/PostMapper.xml"/> | |
</mappers> |
mybatis官方url定位介绍
ResultMap
问题
当pojo中的字段与数据库中的字段不同时,则可能会导致项目某些功能运行失败
因为字段名不一致,运行结果导致结果查询失败
解决方法
方法一
(起别名)
select * from Users
相当于
select id,name,pwd from Users
为此,我们必须为pwd起一个别名
select id,name,pwd as password from Users
然后就可以查询到password这个字段对应在数据库pwd中的值了
方法二;
(更改前)使用ResultMap集合
<select id="getUsersDaoList" resultType="users"> | |
select id,name, pwd as password from Users | |
</select> |
(更改后)其中resultType更换为resultMap
<select id="getUsersDaoList" resultMap="usersResultMap"> | |
select id,name, pwd from Users | |
</select> |
然后追加一个属性
<resultMap id="" type=""> | |
<result column="" property=""/> | |
</resultMap> |
此处标签属性介绍
id - 相当于绑定标识 -在这里绑定到 resultMap=“usersResultMap” -即id=“usersResultMap”
type-相当于返回类型–返回类型为实体类中的类—type =“Users”
column–列表,数据库中的字段–column=“pwd”
property–属性,java类中字段名–property=“password”
<resultMap id="usersResultMap" type="users"> | |
<result column="id" property="id"/> | |
<result column="name" property="name"/> | |
<result column="pwd" property="password"/> | |
</resultMap> |
当然,在我们这个实例当中,字段id和name和数据库中的字段并没有区别,所以我们可以适当进行删减
<resultMap id="usersResultMap" type="users"> | |
<result column="pwd" property="password"/> | |
</resultMap> |
这样就体现到了resultMap的一个优点
是否使用resultMap
当连表查询等复杂查询时,建议使用resultMap 进行项目创建
当仅仅是简单查询时,不建议使用resultMap
为了保证项目的简洁,建议在创建实体类时,最后将数据库和实体类中的字段名一一对应。
日志
日志工厂
日志简而言之是提供操作记录,有时便于我们进行排错
日志工厂在设置中进行添加,即
key–> logImpl
value—>
- SLF4J
- LOG4J (常用)
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING (常用,标准)
- NO_LOGGING
重要:使用日志工厂时,必须保证大小写,数量一致,空格多少也会导致错误。
STDOUT_LOGGING
STDOUT_LOGGING
<settings> | |
<setting name="logImpl" value="STDOUT_LOGGING"/> | |
</settings> |
观察控制台输出结果
前部分一些处理(暂且不看)
总体来说便于理解如何实现SQL查询
LOG4J
什么是LOG4J
- Log4j是Apache的一个开发源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的实践记录器、UNIX Syslog守护进程等;
- 我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
- 可以通过一个配置文件进行灵活的配置
导入LOG4J包
<!-- https://mvnrepository.com/artifact/log4j/log4j --> | |
<dependency> | |
<groupId>log4j</groupId> | |
<artifactId>log4j</artifactId> | |
<version>1.2.17</version> | |
</dependency> |
配置LOG4J配置
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/caiji.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 |
将大括号改为英文下的
运行后可能出现的问题
日志目录下的log存在打不开的现象
出现的原因可能有
- 在Mybatis核心配置文件当中<typeAlices 别名采用的是包扫描的方法
- 在Mybatis核心配置文件当中<mapper 扫描资源时使用的是包扫描方法
解决
- 将包扫描改为其他方式查找资源
LOG4J日志化打印格式
Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下:
%m输出代码中指定的消息;
%M输出打印该条日志的方法名;
%p输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL;
%r输出自应用启动到输出该log信息耗费的毫秒数;
%c输出所属的类目,通常就是所在类的全名;
%t输出产生该日志事件的线程名;
%n输出一个回车换行符,Windows平台为"rn”,Unix平台为"n”;
%d输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy-MM-ddHH:mm:ss,SSS},输出类似:2002-10-1822:10:28,921;
%l输出日志事件的发生位置,及在代码中的行数。
LOG4J简单应用
首先需要一个Logger对象,获取当前类的信息,并通过这个对象来进行操作日志
static Logger logger = Logger.getLogger(Text.class); //因为日志好多都需要使用,所以要提升作用域
然后进行一个简单的测试
static Logger logger = Logger.getLogger(Text.class); | |
@Test | |
public void TestLOG4J(){ | |
logger.info("INFO:进入方法"); | |
logger.debug("DEBUG:进入方法"); | |
logger.error("ERROR:进入方法"); | |
} |
其中,logger.info logger.debug logger.error 相当于java中的System.out.println()
测试结果
log日志
输出了方法
其中,优先级 Error >Info>debug
OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL 以此递减
Limit分页
首先,如果使用的是SQLServer数据库,那么他的分页方法有四种,并不是Limit关键字进行分页
limit关键字是mySql数据库进行分页操作时的关键字。
在这里使用SQLServer数据库进行分页查询
使用map为参数查询
编写分页查询接口
//分页 | |
List<Users> getUsersLimit(Map<String,Integer> map); |
采用传递Map接口来传递参数更快捷简单
编写接口配置文件
<select id="getUsersLimit" parameterType="map" resultMap="usersResultMap" > | |
select top ${pageSize} * from Users where id not in(select top ${pageIndex} id from Users ORDER BY id) | |
</select> |
其中usersResultMap绑定在resultMap当中,用于上一次解决字段名不相同问题
{pageSize} 以及 {pageIndex} 为自定义名字,用于传递参数,在测试类当中我们将以此为Key值进行传递参数。
需要注意的是:
在编写SQL语句时,一般在数据库具有字段可变参格式 例如,id=#{id}
自行构造参数传递时,例如分页查询,其传递参数仅仅为#{pageSize}
当SQL语句当中top 等关键字出现时,传递的参数格式就需要 ${pageSize}
原因是top关键字后占位符不能使用#{}来进行占位 但是有可能会有注入问题出现
编写Test测试
@Test | |
public void TestLimit(){ | |
SqlSession sqlSession = UsersUtils.getSqlSession(); | |
UsersMapper mapper = sqlSession.getMapper(UsersMapper.class); | |
HashMap<String,Integer > map = new HashMap<String,Integer >(); | |
map.put("pageSize",2); | |
map.put("pageIndex",1); | |
List<Users> us = mapper.getUsersLimit(map); | |
for (Users u : us) { | |
System.out.println(u); | |
} | |
sqlSession.close(); | |
} |
RowBounds分页查询(了解)
RowBounds查询属于面向对象查询(官方不建议使用)
编写Mapper接口
List<Users> getRowBounds();
编写接口配置文件即SQL
<select id="getRowBounds" resultMap="usersResultMap"> | |
select * from Users | |
</select> |
测试类
简单查询分页,没有调用UsersMapper接口中的方法,不需要获取其反射对象
public void TestRowBounds(){ | |
SqlSession sqlSession = UsersUtils.getSqlSession(); | |
RowBounds rowBounds = new RowBounds(1,2); | |
//另一种获取Mapper接口的方法 //权限命名 | |
List<Users> usersList = sqlSession.selectList("com.Dao.UsersMapper.getRowBounds", null, rowBounds); | |
for (Users users : usersList) { | |
System.out.println(users); | |
} | |
sqlSession.close(); | |
} |
注解开发
一个简单的注解开发案例
编写接口
//注解开发 | |
@Select("select * from Users") | |
List<Users> getUsersList(); |
将mybatis核心配置文件的映射部分改为Class映射
<mappers> | |
<mapper class="com.Dao.UsersMapper"></mapper> | |
</mappers> |
编写测试类
@Test | |
public void TestCom(){ | |
SqlSession sqlSession = UsersUtils.getSqlSession(); | |
UsersMapper mapper = sqlSession.getMapper(UsersMapper.class); | |
List<Users> List = mapper.getUsersList(); | |
for (Users users : List) { | |
System.out.println(users); | |
} | |
sqlSession.close(); | |
} |
CURD
编写接口
//注解开发 | |
@Select("select * from Users") | |
List<Users> getUsersList(); | |
//增 | |
@Insert("Insert Users (id,name,pwd) values(#{id},#{name},#{password})") | |
int insertUsers(Users users); | |
//改 | |
@Update("update Users set name=#{name},pwd=#{password} where id=#{id}") | |
int updateUsers(Users users); | |
//删 | |
@Delete("delete from Users where id=#{id}") | |
int delectUsers(@Param("id") int id); |
@Param参数注解,后面参数名称
测试类
省略
lombok
多对一的处理
搭建复杂SQL查询环境
创建Student表
create table Student( | |
id int not NULL primary key, | |
name varchar(20) Null, | |
tid int null | |
) |
创建Teacher表
create table Teacher( | |
id int not NULL primary key, | |
name varchar(20) null | |
) |
创建外键
alter table Student | |
add constraint FK_Student_tid foreign key(tid) references Teacher (id) |
association 多对一 多个学生面对同一个老师
collection 集合 一对多 一个老师拥有多个学生
实体类
public class Student { | |
private int id; | |
private String name; | |
private Teacher teacher; | |
} | |
public class Teacher { | |
private int id; | |
private String name; | |
} |
按照查询嵌套处理
<select id="getStudentList" resultMap="TeacherStudent"> | |
select * from Student | |
</select> | |
<select id="getTeacher" resultType="Teacher"> | |
select * from Teacher where id=#{tid} | |
</select> | |
<resultMap id="TeacherStudent" type="Student"> | |
<result property="id" column="id"></result> | |
<result property="name" column="name"></result> | |
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"></association> | |
</resultMap> |
按照结果嵌套处理
<select id="getStudentInformation" resultMap="TeacherStudent2"> | |
select s.id sid, s.name sname, t.name tname from Student s,Teacher t where s.tid=t.id | |
</select> | |
<resultMap id="TeacherStudent2" type="Student"> | |
<result property="id" column="sid"/> | |
<result property="name" column="sname"/> | |
<association property="teacher" column="tname" javaType="Teacher"> | |
<result property="name" column="tname"/> | |
</association> | |
</resultMap> |
接口类
public interface StudentMapper { | |
//获取学生别表 | |
// @Select("select * from Student") | |
List<Student> getStudentList(); | |
//获取学生信息 | |
List<Student> getStudentInformation(); | |
} |
一对多处理
一个老师拥有多个学生
collection 集合
实体类
public class Student { | |
private int id; | |
private String name; | |
} | |
public class Teacher { | |
private int id; | |
private String name; | |
private List<Student> students; | |
} |
按照查询嵌套处理
<select id="getTeacherList" resultMap="TeacherStudent"> | |
select * from Teacher where id= | |
</select> | |
<resultMap id="TeacherStudent" type="Teacher"> | |
<collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudent"/> | |
</resultMap> | |
<select id="getStudent" resultType="Student"> | |
select * from Student where tid = | |
</select> |
按照结果嵌套处理
<select id="getList" resultMap="List"> | |
select s.id sid, s.name sname ,t.id tid ,t.name tname from Student s,Teacher t where s.tid=t.id and s.tid=#{id} | |
</select> | |
<resultMap id="List" type="Teacher"> | |
<result property="id" column="tid"/> | |
<result property="name" column="tname"/> | |
<collection property="students" column="tid" ofType="Student" > | |
<result property="id" column="sid"/> | |
<result property="name" column="sname"/> | |
</collection> | |
</resultMap> |
接口类
public interface TeacherMapper { | |
//获取老师列表 | |
List<Teacher> getTeacherList(int id); | |
//查询全部 | |
List<Teacher> getList(int id); | |
} |
动态SQL环境搭建
什么是动态SQL
动态SQL就是根据不同的条件生成不同的SQL语句
创建表
利用mybatis 添加数据
pojo实体类
public class Blog { | |
private String id; | |
private String title; | |
private String author; | |
private Date createTime; | |
private int views; | |
} |
数据库中的字段名与实体类中的横杠不统一
createTime----create_time
解决方法
<setting name="mapUnderscoreToCamelCase" value="true"/>
在mybatis-config.xml核心配置文件当中设置
开启驼峰转换
BlogMapper
//添加Blog用户 | |
int addBlogList(Blog blog); |
BlogMapper.xml
<insert id="addBlogList" parameterType="Blog" > | |
insert Blog(id,title,author,create_time,views)values(#{id},#{title},#{author},#{createTime},#{views}) | |
</insert> |
测试类
//添加用户 | |
public void addBlogList(){ | |
SqlSession sqlSession = UserUtils.getSqlSession(); | |
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); | |
Blog blog = new Blog(); | |
blog.setId(IDUtils.getUUid()); | |
blog.setTitle("JAVA的入门"); | |
blog.setAuthor("咸鱼"); | |
blog.setCreateTime(new Date()); | |
blog.setViews(9999); | |
mapper.addBlogList(blog); | |
blog.setId(IDUtils.getUUid()); | |
blog.setTitle("JAVA的开始"); | |
blog.setAuthor("咸鱼"); | |
blog.setCreateTime(new Date()); | |
blog.setViews(8080); | |
mapper.addBlogList(blog); | |
blog.setId(IDUtils.getUUid()); | |
blog.setTitle("JAVA的进阶"); | |
blog.setAuthor("咸鱼"); | |
blog.setCreateTime(new Date()); | |
blog.setViews(6666); | |
mapper.addBlogList(blog); | |
blog.setId(IDUtils.getUUid()); | |
blog.setTitle("JAVA的精通"); | |
blog.setAuthor("咸鱼"); | |
blog.setCreateTime(new Date()); | |
blog.setViews(9999); | |
mapper.addBlogList(blog); | |
sqlSession.close(); | |
} |
动态SQL—if
当我们想要做一个类似于查找的功能时,例如通过title和author进行模糊查找
我们可以选择只填写title 或者只填写author
如果仅仅采用编写多个方法来应对不同的需求时,那么这些代码将有很多重复,不简洁
select * from User where title=? and author=?
select * from User where title=?
select * from User where author=?
这样写一个功能 使用3个sql进行多次查询?
这样也太麻烦了
为此
我们将采用动态sql中的if查询
同样是上侧的案例
接口方法:(采用map作为参数进行传值,通过put进行动态添加key,value)
//动态SQLif | |
List<Blog> getList(Map map); |
mapper.xml
<select id="getList" parameterType="map" resultType="Blog"> | |
select * from Blog where 1=1 | |
<if test="title!=null"> | |
and title=#{title} | |
</if> | |
<if test="author!=null" > | |
and author=#{author} | |
</if> | |
</select> |
首先,先书写一个用于查询全部数据的SQL语句,然后在后添加一个Where 1=1 ,添加where 1=1 的目的就是在没有任何条件下的查询语句 ,其作用和 select * from Blog 作用相等(但是在项目书写代码中,where 1=1并不符合规范,这里使用仅仅是为了学习if,接下来会有解决这个问题的知识。)
然后在使用if语句进行判断,当有title这个值存在时,就把他拼接进去。同理 author也是如此。
这样就使用一个sql语句就解决带有N多种条件的查询啦!
动态SQL—where
where
解决where 1=1 的问题
//动态SQLif2 where标签 | |
List<Blog> getList2(Map map); | |
<select id="getList2" parameterType="map" resultType="Blog"> | |
select * from Blog | |
<where> | |
<if test="title!=null"> | |
and title =#{title} | |
</if> | |
<if test="author!=null" > | |
and author=#{author} | |
</if> | |
</where> | |
</select> |
添加where标签,可以在if满足的条件下,自动添加where标签。并且自动检测是否在第一项,自动省略第一个条件的and标签。
另外,有时候where自动省略and 或者or 标签失败。
所以,解决此问题的方法自定义设定
<trim prefix="WHERE" prefixOverrides="AND |OR "> | |
... | |
</trim> |
prefix------前缀
prefixOverrides------前缀覆盖
set
//动态SQL update set标签 | |
int updateList(Map map); | |
<update id="updateList" > | |
update Blog | |
<set> | |
<if test="title!=null">title=#{title},</if> | |
<if test="author!=null">author=#{author}</if> | |
</set> | |
where id=#{id} | |
</update> |
自动将后面的,进行覆盖或者显现
<trim prefix="SET" suffixOverrides=","> | |
... | |
</trim> |
choose、when、otherwise
//choose选择 | |
List<Blog> getListChoose(Map map); | |
<select id="getListChoose" parameterType="map" resultType="Blog"> | |
select * from Blog | |
<where> | |
<choose> | |
<when test="title!=null"> and title=#{title}</when> | |
<when test="author!=null">and author=#{author}</when> | |
<otherwise> | |
and views=#{views} | |
</otherwise> | |
</choose> | |
</where> | |
</select> |
choose选择器,相当于java中的while 当第一中符合时,其余的都不会有效
当有一种符合时,其他都会无效。
SQL片段
//sql片段。实现sql复用 | |
List<Blog> getList3(Map map); | |
<select id="getList3" parameterType="map" resultType="Blog"> | |
select * from Blog | |
<where> | |
<include refid="if-title-author"></include> | |
</where> | |
</select> | |
<sql id="if-title-author"> | |
<if test="title!=null"> | |
and title =#{title} | |
</if> | |
<if test="author!=null" > | |
and author=#{author} | |
</if> | |
</sql> |
定义sql以实现sql 的复用
最好不要将where等标签放入sql语句当中,以提供sql语句的多次复用
foreach(*
//sqlforeach查询 | |
List<Blog> getList4(Map map); | |
<select id="getList4" parameterType="map" resultType="Blog"> | |
select * from Blog | |
<where> | |
<foreach collection="ids" item="id" open="and (" close=")" separator="or"> | |
id= | |
</foreach> | |
</where> | |
</select> |
测试
//sqlforeach | |
@Test | |
public void getIfListTest4(){ | |
SqlSession sqlSession = UserUtils.getSqlSession(); | |
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); | |
HashMap map = new HashMap(); | |
ArrayList ids = new ArrayList(); | |
ids.add("1"); | |
ids.add("2"); | |
map.put("ids",ids); | |
List<Blog> list2 = mapper.getList4(map); | |
for (Blog blog : list2) { | |
System.out.println(blog); | |
} | |
sqlSession.close(); | |
} |
在这里分隔符需要使用or 其他可能会报错
动态SQL,就是在拼接SQL,保证SQL的正确性,按照SQL格式
缓存
在Mybatis当中,分为一级缓存和二级缓存
一级缓存
一级缓存和SQLsession同级别
在创建session和关闭session之间有效。
缓存可以缓存所有的select语句,每当出现update、insert、delect出现时,缓存将会失效,将会被刷新
一级缓存是默认缓存
二级缓存
虽然在设置当中默认开启了二级缓存的总开关,但是为了保证代码的可读性,建议在xml核心配置文件当中设置
<setting name="cacheEnabled" value="true"/>
在Mapper里添加下面的标签开启二级缓存
<cache/>
Mybatis缓存原理
(首次)开启sqlsession查询数据库,然后存入一级缓存,当SQLsession关闭后,一级缓存将会存入该Mapper中的二级缓存当中。
(再次)查询过程
r">
and title =#{title} and author=#{author} ```
定义sql以实现sql 的复用
最好不要将where等标签放入sql语句当中,以提供sql语句的多次复用
foreach(*
//sqlforeach查询 | |
List<Blog> getList4(Map map); | |
<select id="getList4" parameterType="map" resultType="Blog"> | |
select * from Blog | |
<where> | |
<foreach collection="ids" item="id" open="and (" close=")" separator="or"> | |
id= | |
</foreach> | |
</where> | |
</select> |
测试
//sqlforeach | |
@Test | |
public void getIfListTest4(){ | |
SqlSession sqlSession = UserUtils.getSqlSession(); | |
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); | |
HashMap map = new HashMap(); | |
ArrayList ids = new ArrayList(); | |
ids.add("1"); | |
ids.add("2"); | |
map.put("ids",ids); | |
List<Blog> list2 = mapper.getList4(map); | |
for (Blog blog : list2) { | |
System.out.println(blog); | |
} | |
sqlSession.close(); | |
} |
[外链图片转存中…(img-vgNIQlbm-1703315948656)]
在这里分隔符需要使用or 其他可能会报错
动态SQL,就是在拼接SQL,保证SQL的正确性,按照SQL格式
缓存
在Mybatis当中,分为一级缓存和二级缓存
一级缓存
一级缓存和SQLsession同级别
在创建session和关闭session之间有效。
缓存可以缓存所有的select语句,每当出现update、insert、delect出现时,缓存将会失效,将会被刷新
一级缓存是默认缓存
二级缓存
[外链图片转存中…(img-TXo9UTXP-1703315948657)]
虽然在设置当中默认开启了二级缓存的总开关,但是为了保证代码的可读性,建议在xml核心配置文件当中设置
<setting name="cacheEnabled" value="true"/>
在Mapper里添加下面的标签开启二级缓存
<cache/>
Mybatis缓存原理
(首次)开启sqlsession查询数据库,然后存入一级缓存,当SQLsession关闭后,一级缓存将会存入该Mapper中的二级缓存当中。
(再次)查询过程
先从二级缓存中查询是否有此查询的缓存,若没有,然后在一次缓存当中查询是否有此缓存,如果都没有,那么连接数据库,进行查询。