三层架构来源于后端开发的一种分层的思想,将整个业务应用划分为:界面层(User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer),目的是为了"高内聚低耦合"的设计思想,各层的作用如下:
事实上,还有一个隐含的"实体层(Entity)",其实现了面向对象思想中的"封装",贯穿于三层之间用于传递数据。如,要传递员工的信息包括:员工号、姓名、年龄、性别、工资等,此时若使用实体Employee作为参数,则会大大提高效率
MyBatis是一个半自动的ORM(Object Relation Mapping,对象关系映射,指在Java对象和数据库的关系模型之间建立一种对应关系)持久层(DAL)框架,其对JDBC操作数据库的过程进行了封装,使得开发者只需关注SQL本身。我们原来使用JDBC操作数据库,需要手动的写代码去注册驱动、获取connection、获取statement等等,现在Mybaits帮助我们把这些事情做了,我们只需要关注我们的业务sql即可,这样可以提高我们的开发效率。
ORM:Object Relational Mapping,在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了 。
Java概念 | 数据库概念 |
---|---|
类 | 表 |
属性 | 字段/列 |
对象 | 记录/行 |
持久化:Persistence,即,将数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的数据存储在关系型的数据库中,当然也可以存储在磁盘文件中、XML数据文件中等等。
持久层:Persistence Layer,专注于实现数据持久化应用领域的某个特定系统的一个逻辑层面,将数据使用者和数据实体相关联。
与其它持久化层技术的对比:
1.JDBC核心对象
2.MyBatis核心对象
MyBatis完成一次数据库操作所经历的主要步骤如下:
类型 | 说明 |
---|---|
SqlSessionFactoryBuilder | SqlSessionFactoryBuilder的唯一作用就是用来创建SqlSessionFactory,创建完成之后就不会用到它了,所以SqlSessionFactoryBuiler生命周期极短。SqlSessionFactoryBuiler中提供了9个方法,返回的都是SqlSessionFactory对象。 |
SqlSessionFactory | SqlSessionFactory是一个接口,默认的实现类,主要是用来生成SqlSession对象,而SqlSession对象是需要不断被创建的,所以SqlSessionFactory是全局都存在的,也没有必要重复创建,所以这是一个单例对象。 |
SqlSession | SqlSession是用来操作xml文件中我们写好的sql语句,每次操作数据库我们都需要一个SqlSession对象,SqlSession是用来和数据库中的事务进行对接的,所以SqlSession里面是包含了事务隔离级别等信息的。 |
Mapper | Mapper是一个接口,没有任何实现类。主要作用就是用来映射Sql语句的接口,映射器的接口实例从SqlSession对象中获取,所以说Mapper实例作用域是和SqlSession相同或者更小。 |
生命周期
3.MyBatis四大内置对象
以下内容参考博客:https://blog.csdn.net/cwx397562/article/details/100334210
注:这里介绍的主要是(除mappers外)映射文件中的标签,而核心配置文件中的标签放在"编写全局配置文件"一栏中。
MyBatis 是基于 sql 映射配置的框架,sql 语句都写在 Mapper 配置文件中,当构建 SqlSession 类之后,就需要去读取 Mapper 配置文件中的 sql 配置。
而 mappers 标签就是用来配置需要加载的 sql 映射配置文件路径的,其中的每一个mapper子标签都是一个独立的映射配置文件的路径。
1.通过接口所在包配置
<mappers>
<package name="org.example.Mappers"/>
mappers>
使用package标签进行配置,属性name指定mapper接口所在包,要求对应的映射文件必须与接口位于同一路径下,并且名称相同。
2.通过相对路径配置(常用)
这里是将核心配置文件放在src/main/resources下,而mapper映射文件放在resources下的Mappers目录当中。
<mappers>
<mapper resource="Mappers/selectMapper.xml"/>
<mapper resource="Mappers/insertMapper.xml"/>
<mapper resource="Mappers/updateMapper.xml"/>
mappers>
3.类注册引入
mapper标签通过指定mapper接口的名称,此时对应的映射文件必须与接口位于同一路径下,并且名称相同。这里,是将selectMapper、insertMapper、updateMapper接口放在同一UserMapper目录下。
<mappers>
<mapper class="org.example.UserMapper.selectMapper"/>
<mapper class="org.example.UserMapper.insertMapper"/>
<mapper class="org.example.UserMapper.updateMapper"/>
mappers>
4.使用URL绝对路径方式引入(一般不使用)
<mappers>
<mapper url="D:\IntelliJ IDEA\MyBatisDemo\src\main\java\org\example\UserMapper\insertMapper.java"/>
mappers>
collection与association标签的属性一样,都是用于resultMap返回关联映射使用,collection关联的是集合,而association是关联单个对象。
属性:
关联子标签:
MyBatis的基本工作流程:
MyBatis的基本使用有以下四步:
1.1 IDEA创建一个Maven项目
1.2 写入pom.xml文件导入jar包
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.11version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.30version>
dependency>
1.3 创建MyBatis核心配置文件
MyBatis核心配置文件习惯上命名为mybatis-config.xml,这个文件名并非强制要求,事实上,在整合Spring之后,这个配置文件可以省略。
核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息,核心配置文件存放的位置为:src/main/resources目录下,且,MyBatis的核心配置
文件的配置必须按照严格的顺序。
核心配置文件解析过程:
SqlSessionFactoryBuilder # build
|- XMLConfigBuilder # parse
|- XMLConfigBuilder # parseConfiguration
核心配置文件的配置顺序:
配置信息都要写在configuration标签内,且顺序为:
加载方式 | 案例 |
---|---|
使用相对于类路径资源引用(常用) | |
使用Mapper接口的全限定类名,此种方法要求mapper 接口名称和mapper 映射文件名称相同,且放在同一个目录中。 | |
注册指定包下的所有接口,此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。 |
注:有得用"/“表示目录结构,而有的用”."来表示。
核心配置文件(mybatis-config.xml)模板:
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="UserMapper.xml"/>
mappers>
configuration>
注:可在设置->编辑器->文件和代码模板当中保存此模板,而新建时会要求输入参数,即可完成自动创建:
在这里,我创建了专用的资源文件jdbc.properties在src/main/resources目录下(和mybatis-config.xml同目录),可方便后面的修改,降低耦合性:
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatisdemo?useSSL=false&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456
需要在核心配置文件中引入jdbc.properties资源文件:
<properties resource="jdbc.properties"/>
从而得到:
mybatis-config.xml
<configuration>
<properties resource="jdbc.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
mappers>
configuration>
2.1 创建User表
create table tb_user(
id int not null auto_increment primary key ,
username varchar(20),
password varchar(20),
age int,
sex char,
email varchar(20)
);
2.2 创建映射实体类
package org.example.Entity;
public class User {
private Integer Uid;
private String UserName;
private String UPassword;
private Integer UAge;
private String USex;
private String UEmail;
public User() {
}
public User(Integer uid, String userName, String UPassword, Integer UAge, String USex, String UEmail) {
Uid = uid;
UserName = userName;
this.UPassword = UPassword;
this.UAge = UAge;
this.USex = USex;
this.UEmail = UEmail;
}
public Integer getUid() {
return Uid;
}
public void setUid(Integer uid) {
Uid = uid;
}
public String getUserName() {
return UserName;
}
public void setUserName(String userName) {
UserName = userName;
}
public String getUPassword() {
return UPassword;
}
public void setUPassword(String UPassword) {
this.UPassword = UPassword;
}
public Integer getUAge() {
return UAge;
}
public void setUAge(Integer UAge) {
this.UAge = UAge;
}
public String getUSex() {
return USex;
}
public void setUSex(String USex) {
this.USex = USex;
}
public String getUEmail() {
return UEmail;
}
public void setUEmail(String UEmail) {
this.UEmail = UEmail;
}
@Override
public String toString() {
return "User{" +
"Uid=" + Uid +
", UserName='" + UserName + '\'' +
", UPassword='" + UPassword + '\'' +
", UAge=" + UAge +
", USex='" + USex + '\'' +
", UEmail='" + UEmail + '\'' +
'}';
}
}
2.3 创建接口
MyBatis中的mapper接口相当于以前的dao层,但是区别在于mapper仅仅是接口,我们不需要提供其实现类,事实上,MyBatis有面向接口编程的功能,故而,只要调用接口中的方法,它就会自动匹配一个相映射的SQL语句并执行。
这里我放在了src/main/java/UserMapper目录下:
package org.example.UserMapper;
public interface insertMapper {
/*
* 添加用户信息
* */
int insertUser();
}
2.4 创建MyBatis的映射文件
MyBatis映射文件用于编写SQL,用来访问以及操作表中的数据,且,MyBatis的映射文件的一般存放位置是:src/main/resources/mappers目录下。
mapper映射文件的解析过程
XMLConfigBuilder # mapperElement
|- XMLMapperBuilder # parse
|- XMLMapperBuilder # configurationElement
映射文件的命名规则
表所对应的实体类的类+Mapper.xml,如,表tb_user,其映射的实体类为User,则其所对应的映射文件为UserMapper.xml。因此,一个映射文件对应一个实体类,对应一张表上的操作。
MyBatis中面向接口操作数据的两个前提:
官方文档的映射文件:
<mapper namespace="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
select>
mapper>
可将其设置在IDEA的模板当中。
创建User实体类的映射文件:
在src/main/resources目录下创建UserMapper.xml,这里,我的项目结构为:
<mapper namespace="org.example.UserMapper.insertMapper">
<insert id="insertUser">
insert into tb_user values (null,'admin','123456',18,'男','[email protected]')
insert>
mapper>
在mybatis-config.xml当中引入映射文件
<mappers>
<mapper resource="UserMapper.xml"/>
mappers>
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.example.UserMapper.insertMapper;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class MyBatisTest {
@Test
public void test1() throws IOException {
//加载核心配置文件
InputStream is= Resources.getResourceAsStream("mybatis-config.xml");
//获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
//获取SqlSessionFactory对象,是"生产"SQLSession的"工厂".
SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(is);
//获取SqlSession对象,代表Java程序和数据库之间的会话(类似于HttpSession是Java程序和浏览器之间的会话).
SqlSession sqlSession=sqlSessionFactory.openSession();
//工厂模式:如果创建某一个对象,使用的过程基本固定,那么就可以把创建这个对象的相关代码封装到一个"工厂类"中,以后都使用这个工厂类"生产我们需要的对象.
//事实上,数据库都是通过会话来与数据库交互,创建一个会话就会把该会话加载到内存中,当中有许多监听器监听会话过程.
//获取mapper接口对象
insertMapper mapper=sqlSession.getMapper(insertMapper.class);
//测试功能
int result=mapper.insertUser();
//提交事务,因为使用的是JDBC管理方式,需要手动提交
sqlSession.commit();
System.out.println(result);
}
}
运行结果:
注,若要实现自动提交功能,可在获取SqlSession对象时进行设置:
SqlSession sqlSession=sqlSessionFactory.openSession(true);
将获取SqlSession对象的操作进行封装,可大大简化代码。
SqlSessionUtils.java
package org.example.MyBatisUtils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class SqlSessionUtils {
public static SqlSession getSqlSession() throws IOException {
SqlSession sqlSession=null;
try{
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
sqlSession=sqlSessionFactory.openSession(true);
}catch (IOException e){
e.printStackTrace();
}
return sqlSession;
}
}
Log4J 是 Apache 的一个开源项目,通过在项目中使用 Log4J,我们可以控制日志信息输出到控制台、文件、GUI 组件、甚至是数据库中。我们可以控制每一条日志的输出格式,通过定义日志的输出级别,可以更灵活的控制日志的输出过程。方便项目的调试。
Log4J 主要由 Loggers (日志记录器)、Appenders(输出端)和 Layout(日志格式化器)组成。其中 Loggers 控制日志的输出级别与日志是否输出;Appenders 指定日志的输出方式(输出到控制台、文件等);Layout 控制日志信息的输出格式。
Log4J在org.apache.log4j.Level类中定义了OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL七种日志级别:
日志级别 | 说明 |
---|---|
OFF | 最高日志级别,关闭左右日志。 |
FATAL | 将会导致应用程序退出的错误。 |
ERROR | 发生错误事件,但仍不影响系统的继续运行。 |
WARN | 警告,即潜在的错误情形。 |
INFO | 一般用于粗粒度级别上,强调应用程序的运行全程。 |
DEBUG | 一般用于细粒度级别上,对调试应用程序非常有帮助 |
ALL | 最低等级,打开所有日志记录。 |
注:一般只用ERROR、WARN、INFO、DEBUG四个日志级别,优先级从高到低为 ERROR > WARN > INFO > DEBUG。
Appender(输出端),用来指定日志输出到哪个地方,可以同时指定日志的输出目的地。
1.org.apache.log4j.ConsoleAppender(控制台)
2.org.apache.log4j.FileAppender(文件)
3.org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
4.org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
5.org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
Log4j 常用的输出目的地有以下几种:
输出端类型 | 作用 |
---|---|
ConsoleAppender | 将日志输出到控制台。 |
FileAppender | 将日志输出到文件中。 |
DailyRollingFileAppender | 将日志输出到一个日志文件,并且每天输出到一个新的文件。 |
RollingFileAppender | 将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大 小达到指定尺寸时,会自动把文件改名,同时产生一个新的文件。 |
JDBCAppender | 将日志信息保存到数据库中。 |
Layout(日志格式化器),用于将日志信息格式化。
1.org.apache.log4j.HTMLLayout(以HTML表格形式布局),
2.org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
3.org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
4.org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
格式化器类型 | 作用 |
---|---|
HTMLLayout | 格式化日志输出为HTML表格形式。 |
SimpleLayout | 简单的日志输出格式化,打印的日志格式为(info - message)。 |
PatternLayout | 最强大的格式化期,可以根据自定义格式输出日志,如果没有指定转换格式,就是用默认的转换格式。 |
常用输出格式:
Log4J最常用的日志输出格式为:org.apache.log4j.PatternLayOut,其主要参数为:
1.引入依赖
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
2.配置文件
创建配置文件log4j-config.properties。
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/lding.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
3.mybatis-config.xml配置
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
SQL所需的参数可以通过相应的接口进行传递,此时需要在映射文件中获取这些参数值,并将其拼接到相应的SQL语句当中,最后执行SQL语句。
1.使用${}获取
使用${}来获取参数值的本质是使用字符串拼接的方式拼接SQL,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号。
2.使用#{}获取
使用#{}获取参数值的本质是占位符赋值,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号。
注:以上两种实际上就是JDBC中获取参数值的方式。
注:由于映射文件当中mapper的namespace属性必须写全类名,故,当不同查询语句在不同的接口当中时,需要为每一个接口创建映射文件,项目结构:
若mapper接口的方法参数为单个的字面量类型,此时可以使用${}和#{}以任意的名称获取参数的值,注意,${}需要手动改添加单引号。
Mapper接口:
package org.example.UserMapper;
import org.example.Entity.User;
import java.util.List;
public interface selectMapper {
/*
* 根据用户名查询用户信息
* */
User getUserByUsername(String username);
}
1.使用#{}
创建接口映射文件selectMapper.xml
<mapper namespace="org.example.UserMapper.selectMapper">
<select id="getUserByUsername" resultType="org.example.Entity.User">
select * from tb_user where username=#{username}
select>
mapper>
添加至核心配置文件mybatis-config.xml
<mapper resource="Mappers/selectMapper.xml"/>
编写测试类MyBatisTest.java
import org.apache.ibatis.session.SqlSession;
import org.example.Entity.User;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.UserMapper.selectMapper;
import org.junit.Test;
import java.io.IOException;
public class MyBatisTest {
@Test
public void test1() throws IOException {
SqlSession sqlSession= SqlSessionUtils.getSqlSession();
selectMapper mapper=sqlSession.getMapper(selectMapper.class);
User user=mapper.getUserByUsername("admin");
System.out.println(user);
}
}
2.使用${}
${}的底层原理是使用字符串拼接,故需要手动改添加单引号(不太建议使用,因为可能有SQL注入发生)。
<select id="getUserByUsername" resultType="org.example.Entity.User">
select * from tb_user where username='${username}'
select>
若mapper接口的方法参数为多个时,此时MyBatis会自动将这些参数放在一个map集合中,以arg0,arg1,…为键,以参数为值;以param1,param2,…为键,以参数为值,因此只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号。
注:两种方式的索引并不一致。
package org.example.UserMapper;
import org.example.Entity.User;
import java.util.List;
public interface selectMapper {
/*
* 根据用户名、密码查询用户信息
* */
User getUserByUsernameAndPassword(String username,String password);
}
1.使用#{}
selectMapper.xml
<mapper namespace="org.example.UserMapper.selectMapper">
<select id="getUserByUsernameAndPassword" resultType="org.example.Entity.User">
select * from tb_user where username=#{arg0} and password=#{param2};
select>
mapper>
MyBatisTest.java
import org.apache.ibatis.session.SqlSession;
import org.example.Entity.User;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.UserMapper.selectMapper;
import org.junit.Test;
import java.io.IOException;
public class MyBatisTest {
@Test
public void test1() throws IOException {
SqlSession sqlSession= SqlSessionUtils.getSqlSession();
selectMapper mapper=sqlSession.getMapper(selectMapper.class);
User user=mapper.getUserByUsernameAndPassword("admin","123456");
System.out.println(user);
}
}
2.使用${}
<mapper namespace="org.example.UserMapper.selectMapper">
<select id="getUserByUsername" resultType="org.example.Entity.User">
select * from tb_user where username='${username}'
select>
<select id="getUserByUsernameAndPassword" resultType="org.example.Entity.User">
select * from tb_user where username='${arg0}' and password='${param2}';
select>
mapper>
若mapper接口方法的参数有多个时,可以手动将这些参数放在一个集合map当中存储,而访问这些参数时通过键来进行访问。
接口方法
package org.example.UserMapper;
import org.example.Entity.User;
import java.util.List;
import java.util.Map;
public interface selectMapper {
/*
* 根据用户名、密码、年龄查询用户信息
* */
User getUserByMapOfUsernameAndPasswordAndId(Map<String,Object> map);
}
核心配置文件mybatis-config.xml
<mappers>
<mapper resource="Mappers/selectMapper.xml"/>
mappers>
1.使用#{}
<mapper namespace="org.example.UserMapper.selectMapper">
<select id="getUserByMapOfUsernameAndPasswordAndId" resultType="org.example.Entity.User">
select * from tb_user where username=#{username} and password=#{password} and id=#{id}
select>
mapper>
测试代码
import org.apache.ibatis.session.SqlSession;
import org.example.Entity.User;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.UserMapper.selectMapper;
import org.junit.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class MyBatisTest {
@Test
public void test1() throws IOException {
SqlSession sqlSession= SqlSessionUtils.getSqlSession();
selectMapper mapper=sqlSession.getMapper(selectMapper.class);
Map<String,Object>map=new HashMap<>();
map.put("username","admin");
map.put("password","123456");
map.put("id",1);
User user=mapper.getUserByMapOfUsernameAndPasswordAndId(map);
System.out.println(user);
}
}
2.使用${}
<mapper namespace="org.example.UserMapper.selectMapper">
<select id="getUserByMapOfUsernameAndPasswordAndId" parameterType="map" resultType="org.example.Entity.User">
select * from tb_user where username='${username}' and password='${password}' and id='${id}'
select>
mapper>
在接收实体类参数时,要求映射文件中SQL语句的参数名为实体类的属性名。
package org.example.UserMapper;
import org.example.Entity.User;
public interface insertMapper {
/*
* 添加用户信息
* */
int insertUser(User user);
}
1.使用#{}
insertMapper.xml
<mapper namespace="org.example.UserMapper.insertMapper">
<insert id="insertUser">
insert into tb_user values (null,#{UserName},#{UPassword},#{UAge},#{USex},#{UEmail});
insert>
mapper>
测试代码
import org.apache.ibatis.session.SqlSession;
import org.example.Entity.User;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.UserMapper.insertMapper;
import org.junit.Test;
import java.io.IOException;
public class MyBatisTest {
@Test
public void test1() throws IOException {
SqlSession sqlSession= SqlSessionUtils.getSqlSession();
insertMapper mapper=sqlSession.getMapper(insertMapper.class);
int result= mapper.insertUser(new User(null,"李四","123",23,"男","[email protected]"));
System.out.println(result);
}
}
2.使用${}
<mapper namespace="org.example.UserMapper.insertMapper">
<insert id="insertUser">
insert into tb_user values (null,'${UserName}','${UPassword}',${UAge},'${USex}','${UEmail}');
insert>
mapper>
这是在写"各种插入功能的实现"时发现的情况。
场景:我所插入的Student实体类中包含一个Clazz对象属性(遵循"多对一"原则),而Student表包含ClazzNo字段,我需要在插入Student对象时同时设置它的ClazzNo字段(这是不能通过创建它的Clazz对象属性能实现的),故接口的方法声明为:
package org.example.StudentMapper;
import org.example.Entity.Student;
public interface insertStudentMapper {
/*
* 用实体类对象进行插入
* */
int insertByStudent(Student student,int ClazzNo);
}
insertStudentMapper.xml
<mapper namespace="org.example.StudentMapper.insertStudentMapper">
<insert id="insertByStudent" useGeneratedKeys="true" keyProperty="arg0.SNo">
insert into student VALUES (null,#{arg0.SName}, #{arg0.SAge}, #{arg0.SBirthday},#{arg1})
insert>
mapper>
MyBatisTest.java
import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.insertStudentMapper;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session= SqlSessionUtils.getSqlSession();
insertStudentMapper mapper=session.getMapper(insertStudentMapper.class);
String str="2004-03-24 23:38:47";
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date Birthday=simpleDateFormat.parse(str);
Student student=new Student(null,"田七",19,Birthday,null);
System.out.println(mapper.insertByStudent(student,301));
System.out.println(student.getSNo());
}
}
可见,需要使用"arg0.属性名称"的形式进行获取。
当使用注解开发时,若需要传入多个参数,可以结合@Param注解,@Param标签会被mybatis处理并封装成一个Map对象,以Param中的值为键,以传递的参数为键的值(故在SQL语句当中调用参数时使用的是注解内的值而非函数参数),此时在映射文件中就可用注解中的值进行参数的调用。
insertMapper.java
package org.example.UserMapper;
import org.apache.ibatis.annotations.Param;
public interface insertMapper {
/*
* 添加用户信息
* */
int insertUser(@Param("Uid")int uid,@Param("UserName")String username,@Param("UPassword")String password,@Param("UAge")int age,@Param("USex")String sex,@Param("UEmail")String email);
}
1.使用#{}
<mapper namespace="org.example.UserMapper.insertMapper">
<insert id="insertUser">
insert into tb_user values (null,#{UserName},#{UPassword},#{UAge},#{USex},#{UEmail});
insert>
mapper>
2.使用${}
<mapper namespace="org.example.UserMapper.insertMapper">
<insert id="insertUser">
insert into tb_user values (null,'${UserName}','${UPassword}',${UAge},'${USex}','${UEmail}');
insert>
mapper>
1.前置数据库student_course:
(使用DataGrip编写)
CREATE TABLE `student` (
`SNo` int NOT NULL AUTO_INCREMENT COMMENT '学生学号',
`SName` varchar(20) NOT NULL COMMENT '学生姓名',
`SAge` int NOT NULL COMMENT '学生年龄',
`SBirthday` datetime NOT NULL COMMENT '学生出生日期',
`ClazzNo` int NOT NULL COMMENT '班级序号',
PRIMARY KEY (`SNo`),
KEY `student_clazz__fk` (`ClazzNo`),
CONSTRAINT `student_clazz__fk` FOREIGN KEY (`ClazzNo`) REFERENCES `clazz` (`ClazzNo`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='学生表';
CREATE TABLE `course` (
`CNo` int NOT NULL COMMENT '课程号',
`CName` varchar(20) NOT NULL COMMENT '课程名',
PRIMARY KEY (`CNo`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='课程表';
CREATE TABLE `score` (
`SNo` int NOT NULL AUTO_INCREMENT COMMENT '学生学号',
`CNo` int NOT NULL COMMENT '课程号',
`Score` int NOT NULL DEFAULT '0' COMMENT '学生课程分数',
PRIMARY KEY (`SNo`,`CNo`),
KEY `Score_fk_Course` (`CNo`),
CONSTRAINT `Score_fk_Course` FOREIGN KEY (`CNo`) REFERENCES `course` (`CNo`),
CONSTRAINT `Score_fk_Student` FOREIGN KEY (`SNo`) REFERENCES `student` (`SNo`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='分数表';
CREATE TABLE `clazz` (
`ClazzNo` int NOT NULL COMMENT '班级编号',
`ClazzName` varchar(20) NOT NULL COMMENT '班级名称',
PRIMARY KEY (`ClazzNo`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='班级表';
insert into student values (null,'张三',18,'2005-09-14 23:18:17',301),
(null,'李四',19,'2004-03-12 15:34:13',301),
(null,'王五',17,'2006-12-02 08:20:12',302),
(null,'赵六',18,'2005-02-23 16:14:20',302);
insert into course values (101,'高等数学'),
(102,'线性代数'),
(103,'数据结构'),
(104,'操作系统'),
(105,'计算机组成原理');
insert into score values (1,101,95),
(1,102,85),
(1,103,79),
(1,104,87),
(2,101,91),
(2,103,75),
(2,105,85),
(3,102,85),
(3,104,95),
(4,102,65),
(4,103,78),
(4,105,85);
insert into Clazz values (301,'计科一班'),
(302,'计科二班');
2.创建实体类
Student.java
package org.example.Entity;
import java.util.Date;
public class Student {
private Integer SNo;
private String SName;
private Integer SAge;
private Date SBirthday;
private Clazz clazz;
/*
* 后面内容省略,是表中的JavaBean
* */
Course.java
package org.example.Entity;
public class Course {
private Integer CNo;
private String CName;
/*
* 后面内容省略,是表中的JavaBean
* */
Score.java
package org.example.Entity;
public class Score {
private Integer SNo;
private Integer CNo;
private Integer Score;
/*
* 后面内容省略,是表中的JavaBean
* */
}
Clazz.java
package org.example.Entity;
import java.util.List;
public class Clazz {
private int ClazzNo;
private String ClazzName;
private List<Student> students;
/*
* 后面内容省略,是表中的JavaBean
* */
&emps; 为了能够很好地表现数据表之间的关联,在处理数据表时,会遵循"多对一对应对象,一对多对应集合"的原则。
1.多对一映射
“多对一对应对象”,是指需要在"多"的实体类中创建"一"的实体类对象。
以上述学生-课程数据库为例,多个学生可对应一个班级,故,学生实体类中应包含一个班级实体类对象。
package org.example.Entity;
import java.util.Date;
public class Student {
private Integer SNo;
private String SName;
private Integer SAge;
private Date SBirthday;
private Clazz clazz;
/*
* 后面内容省略,是表中的JavaBean
* */
2.一对多映射
“一对多对应集合”,是指需要在"一"的实体类中创建"多"的实体类对象的集合。
以上述学生-课程数据库为例,一个班级可对应多个学生,故,班级实体类中应包含一个学生实体类对象的集合。
package org.example.Entity;
import java.util.List;
public class Clazz {
private int ClazzNo;
private String ClazzName;
private List<Student> students;
/*
* 后面内容省略,是表中的JavaBean
* */
3.多对多映射
多对多映射时,可新建一张表表示两表关系。
以上述学生-课程数据库为例,学生和课程的分数是多对多关系,故新建一张分数表。
之前一直使用的是resultType来指定返回值类型,但前提是表的字段名要与实体类的属性名相同,而当二者不一致时,就无法将查询结果的字段封装到实体类的对应属性当中。
事实上,可通过resultMap来自定义映射关系,进行手动封装。
1.1 resultMap标签
作用:建立SQL查询结果字段与实体属性的映射关系。
属性名 | 说明 |
---|---|
id | resultMap标签的唯一标识。 |
type | 返回值的全限定类名。 |
autoMapping | 是否开启自动映射功能,即,自动查找与字段名小写同名的属性名,并调用setter方法。默认为true,设置为false后,则需要在resultMap内明确注明映射关系才会调用对应的setter方法。 |
extends | 继承别的resultMap,可选。 |
1.2 id与result子标签
作用:
属性配置:
属性名 | 说明 |
---|---|
property | 需要映射到JavaBean的属性名称。 |
column | 数据表的列名或者标签别名。 |
javaType | 一个 Java 类的全限定名,或一个类型别名。通常不会配置,mybatis 能够根据参数信息自动识别,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
jdbcType | JDBC类型, JDBC类型为CUD操作时列可能为空时进行处理。 |
typeHandler | 指定类型处理器的全限定类名或类型别名。 |
关于什么时候使用jdbcType和JavaType:举一个例子,如果数据库id字段是int类型,那么它的jdbc就是Integer类型。当实体类的这个映射属性id为Long类型时,如果不设置jdbcType和javaType的话,查询的结果返回给实体时就会转换错误,写了这两个mybatis就会帮我们转换成相应的类型,从来避免发生错误。
Mybatis中javaType和jdbcType对应关系
JDBCType JavaType
CHAR String
VARCHAR String
LONGVARCHAR String
NUMERIC java.math.BigDecimal
DECIMAL java.math.BigDecimal
BIT boolean
BOOLEAN boolean
TINYINT byte
SMALLINT short
INTEGER int
BIGINT long
REAL float
FLOAT double
DOUBLE double
BINARY byte[]
VARBINARY byte[]
LONGVARBINARY byte[]
DATE java.sql.Date
TIME java.sql.Time
TIMESTAMP java.sql.Timestamp
CLOB Clob
BLOB Blob
ARRAY Array
DISTINCT mapping of underlying type
STRUCT Struct
REF Ref
DATALINK java.net.URL[color=red][/color]
例:
核心配置文件mybatis-config.xml
<mappers>
<mapper resource="StudentMapper/selectStudentMapper.xml"/>
mappers>
接口selectStudentMapper.java
package org.example.StudentMapper;
import org.example.Entity.Student;
public interface selectStudentMapper {
/*
* 根据学号查询学生
* */
Student getStudentById(Integer SNo);
}
映射文件selectStudentMapper.xml
<mapper namespace="org.example.StudentMapper.selectStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<result property="SBirthday" column="SBirthday"/>
<result property="ClazzNo" column="ClazzNo"/>
resultMap>
<select id="getStudentById" resultMap="StudentResultMap">
select * from student where SNo=#{SNo}
select>
mapper>
测试类
import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session= SqlSessionUtils.getSqlSession();
selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
Student student=mapper.getStudentById(1);
System.out.println(student);
}
}
1.3constructor子标签
constructor子标签,指定使用指定参数列表的构造函数来实例化领域模型。注意:其子元素顺序必须与参数列表顺序对应。
子标签配置
子标签名 | 说明 |
---|---|
idArg | 标记该参数为主键。 |
arg | 标记该参数为普通字段。 |
在之前提到过"多对一映射"的概念,此处就不再赘述。
在本项目中,需要处理的多对一映射是Student实体类中的Clazz实体类属性,即:
package org.example.Entity;
import java.util.Date;
public class Student {
private Integer SNo;
private String SName;
private Integer SAge;
private Date SBirthday;
private Clazz clazz;
/*...*/
selectStudentMapper.xml
<mapper namespace="org.example.StudentMapper.selectStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<result property="clazz.ClazzNo" column="ClazzNo"/>
<result property="clazz.ClazzName" column="ClazzName"/>
resultMap>
<select id="getStudentById" resultMap="StudentResultMap">
select * from student,clazz where student.SNo=#{SNo} and student.ClazzNo=clazz.ClazzNo;
select>
mapper>
测试类:
import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session= SqlSessionUtils.getSqlSession();
selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
Student student=mapper.getStudentById(1);
System.out.println(student);
}
}
association标签是resultMap的子标签,可专门用于处理多对一的映射关系,属性如下:
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<association property="clazz" javaType="org.example.Entity.Clazz">
<id property="ClazzNo" column="ClazzNo"/>
<result property="ClazzName" column="ClazzName"/>
association>
resultMap>
注:javaType属性不能遗漏,不然mybatis将不知道是哪个实体类的属性和实体类表相互对应。
分步查询是建议使用的,其可避免多表连接,效率最高。
2.3.1 属性说明:
2.3.2查询步骤
2.3.2.1 先通过班级编号获取班级实体类
核心配置文件mybatis-config.xml
<mapper resource="ClazzMapper/selectClazzMapper.xml"/>
接口selectClazzMapper.java
package org.example.ClazzMapper;
import org.example.Entity.Clazz;
public interface selectClazzMapper {
/*
* 根据ClazzNo查询班级信息
* */
Clazz getClazzByClazzNo(int ClazzNo);
}
映射文件selectClazzMapper.xml
<mapper namespace="org.example.ClazzMapper.selectClazzMapper">
<resultMap id="getClazzByClazzNo" type="org.example.Entity.Clazz">
<id property="ClazzNo" column="ClazzNo"/>
<result property="ClazzName" column="ClazzName"/>
resultMap>
<select id="getClazzByClazzNo" resultMap="getClazzByClazzNo">
select * from clazz where ClazzNo=#{ClazzNo}
select>
mapper>
2.3.2.2 再通过学生编号获取学生实体类
核心配置文件mybatis-config.xml
<mapper resource="StudentMapper/selectStudentMapper.xml"/>
接口selectStudentMapper.java方法
package org.example.StudentMapper;
import org.example.Entity.Student;
public interface selectStudentMapper {
/*
* 根据学号查询学生
* */
Student getStudentById(Integer SNo);
}
selectStudentMapper.xml
<mapper namespace="org.example.StudentMapper.selectStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<association property="clazz" select="org.example.ClazzMapper.selectClazzMapper.getClazzByClazzNo" column="ClazzNo"/>
resultMap>
<select id="getStudentById" resultMap="StudentResultMap">
select * from student where student.SNo=#{SNo};
select>
mapper>
在之前提到过"一对多映射"的概念,此处就不再赘述。
在本项目中,需要处理的多对一映射是Clazz实体类中的Student实体类集合属性,即:
package org.example.Entity;
import java.util.List;
public class Clazz {
private int ClazzNo;
private String ClazzName;
private List<Student> students;
/*
* 后面内容省略,是表中的JavaBean
* */
接口方法:
package org.example.ClazzMapper;
import org.example.Entity.Clazz;
public interface selectClazzMapper {
/*
* 根据ClazzNo查询班级信息
* */
Clazz getClazzByClazzNo(int ClazzNo);
}
collection专门用来处理一对多的关系,由于collection已经表示集合,故后面只要给出集合的泛型即可,此处使用ofType即可。
属性说明
接口方法
package org.example.ClazzMapper;
import org.example.Entity.Clazz;
public interface selectClazzMapper {
/*
* 根据ClazzNo查询班级信息
* */
Clazz getClazzByClazzNo(int ClazzNo);
}
selectClazzMapper.xml
<mapper namespace="org.example.ClazzMapper.selectClazzMapper">
<resultMap id="getClazzByClazzNo" type="org.example.Entity.Clazz">
<id property="ClazzNo" column="ClazzNo"/>
<result property="ClazzName" column="ClazzName"/>
<collection property="students" ofType="org.example.Entity.Student">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SAge" column="SAge"/>
<result property="SBirthday" column="SBirthday"/>
collection>
resultMap>
<select id="getClazzByClazzNo" resultMap="getClazzByClazzNo">
select * from clazz,student where clazz.ClazzNo=#{ClazzNo} and student.ClazzNo=clazz.ClazzNo
select>
mapper>
注:学生中的Clazz属性不应再获取,否则会栈溢出。
MyBatisTest.java
import org.apache.ibatis.session.SqlSession;
import org.example.ClazzMapper.selectClazzMapper;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.junit.Test;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session= SqlSessionUtils.getSqlSession();
selectClazzMapper mapper=session.getMapper(selectClazzMapper.class);
System.out.println(mapper.getClazzByClazzNo(301));
}
}
3.2.1 先写通过ClazzNo获取所有Student对象
接口方法
package org.example.StudentMapper;
import org.example.Entity.Student;
import java.util.List;
public interface selectStudentMapper {
/*
* 根据班级号查询学生
* */
List<Student> getStudentByClazzNo(Integer ClazzNo);
}
selectStudentMapper.xml
<mapper namespace="org.example.StudentMapper.selectStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
resultMap>
<select id="getStudentByClazzNo" resultMap="StudentResultMap">
select * from student where student.ClazzNo=#{ClazzNo}
select>
mapper>
3.2.2 再通过班级序号获取Clazz对象
接口方法
package org.example.ClazzMapper;
import org.example.Entity.Clazz;
public interface selectClazzMapper {
/*
* 根据ClazzNo查询班级信息
* */
Clazz getClazzByClazzNo(int ClazzNo);
}
selectClazzMapper.xml
<mapper namespace="org.example.ClazzMapper.selectClazzMapper">
<resultMap id="getClazzByClazzNo" type="org.example.Entity.Clazz">
<id property="ClazzNo" column="ClazzNo"/>
<result property="ClazzName" column="ClazzName"/>
<collection property="students" select="org.example.StudentMapper.selectStudentMapper.getStudentByClazzNo" column="ClazzNo"/>
resultMap>
<select id="getClazzByClazzNo" resultMap="getClazzByClazzNo">
select * from clazz where clazz.ClazzNo=#{ClazzNo}
select>
mapper>
3.2.3 测试类
import org.apache.ibatis.session.SqlSession;
import org.example.ClazzMapper.selectClazzMapper;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.junit.Test;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session= SqlSessionUtils.getSqlSession();
selectClazzMapper mapper=session.getMapper(selectClazzMapper.class);
System.out.println(mapper.getClazzByClazzNo(301));
}
}
在映射文件当中,往往需要使用类的全类名,这是非常繁琐的,事实上,可定义别名来简化代码。
<typeAliases>
<typeAlias alias="Course" type="org.example.Entity.Course"/>
typeAliases>
注:别名的作用范围是全局的,需要在核心配置文件当中配置。
我们可以为整个包下的类定义别名,别名默认为类的首字母小写之后的字符串。
<typeAliases>
<package name="org.example.Entity"/>
typeAliases>
在我们的java中已经自己定义了一些常用的数据类型,比如int,long,string等,所以在Mybatis中已经自己嵌入了这些的别名,我们只需要使用的时候来查询一下就可以很好的使用了。
MyBatis框架的动态SQL技术是一种根据特定条件动态拼接SQL语句的功能,其存在的意义是为了解决拼接SQL语句字符串时的痛点问题,然而动态SQL有时候在执行性能 (效率)上面不如静态SQL,而且使用不恰当,往往会在安全方面存在隐患 (SQL 注入式攻击)。
作用:让我们可以在xml映射文件当中以标签的形式编写动态SQL语句,完成逻辑判断和动态拼接 sql 的功能。
原理:根据条件判断语句来动态拼接SQL字符串。
动态SQL标签
元素 | 作用 |
---|---|
if | 单条件分支判断 |
where | 动态生成where |
choose(when、otherwise) | 多条件分支判断,相当于switch-case语句,只会选择满足条件中的一个 |
trim | 用于处理一些SQL前后缀拼装问题 |
foreach | 用于对集合进行遍历。 |
set | 动态生成set |
sql(include) | 用于抽取保存的SQL语句 |
语法格式:
SQL语句
<if test="判断条件">
要拼接的SQL语句
</if>
注意点:
在使用
SELECT * FROM student where and SName=#{SName}
而当所有都不成立时会多出一个where语句:
SELECT * FROM student where
这就需要加上一个where 1=1 的条件,来避免以上两种情况的出现,此时就会查询整张表:
SELECT * FROM student where 1=1;
事实上,后续可使用
案例:传入Student实体类对象,在数据库中查找与之有相同属性的Student对象。
selectStudentMapper.java
package org.example.StudentMapper;
import org.example.Entity.Student;
import java.util.List;
public interface selectStudentMapper {
/*
* 根据If条件筛选出符合条件的学生
* */
List<Student> getStudentByIfCondition(Student student);
}
selectStudentMapper.xml
<mapper namespace="org.example.StudentMapper.selectStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<result property="ClazzNo" column="ClazzNo"/>
resultMap>
<select id="getStudentByIfCondition" resultMap="StudentResultMap" parameterType="org.example.Entity.Student">
select * from student where 1=1
<if test="SNo!=null">
and SNo=#{SNo}
if>
<if test="SName!=null and SName !=''">
and SName=#{SName}
if>
<if test="SAge!=null">
and SAge=#{SAge}
if>
<if test="SBirthday!=null">
and SBirthday=#{SBirthday}
if>
<if test="ClazzNo!=null">
and ClazzNo=#{ClazzNo}
if>
select>
mapper>
MyBatisTest.java
import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.insertStudentMapper;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session= SqlSessionUtils.getSqlSession();
selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
String str="2004-03-12 15:34:13";
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date Birthday=simpleDateFormat.parse(str);
Student student=new Student(2,null,19,Birthday,null,null);
//提供学生的SNo、SAge、SBirthday来查询符合条件的所有学生信息
System.out.println(mapper.getStudentByIfCondition(student));
}
}
但是当所有条件都为null,并将1=1的条件去掉时,会出现:
import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.insertStudentMapper;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session= SqlSessionUtils.getSqlSession();
selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
String str="2004-03-12 15:34:13";
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date Birthday=simpleDateFormat.parse(str);
Student student=new Student(null,null,null,null,null,null);
System.out.println(mapper.getStudentByIfCondition(student));
}
}
在前面的if标签中,需要加上1=1的本质问题是and标签的冗余,且where关键字在没有任何条件时仍然存在,而
标签就很好解决了这个问题。
标签和
共同使用,当
失败后,
关键字只会去掉库表字段赋值前面的and,不会去掉语句后面的and关键字,即注意,
只会去掉
语句中的最开始的and关键字。
且,当只有当
中有返回值时,才会插入where
关键字,且,当
标签返回的SQL字段是以and
或or
开头时,它也会自动剔除。相当于:
<trim prefix="WHERE" prefixOverrides="AND | OR">
...
trim>
案例:
<mapper namespace="org.example.StudentMapper.selectStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<result property="ClazzNo" column="ClazzNo"/>
resultMap>
<select id="getStudentById" resultMap="StudentResultMap">
select * from student where student.SNo=#{SNo};
select>
<select id="getStudentByClazzNo" resultMap="StudentResultMap">
select * from student where student.ClazzNo=#{ClazzNo}
select>
<select id="getStudentByIfCondition" resultMap="StudentResultMap" parameterType="org.example.Entity.Student">
select * from student
<where>
<if test="SNo!=null">
and SNo=#{SNo}
if>
<if test="SName!=null and SName !=''">
and SName=#{SName}
if>
<if test="SAge!=null">
and SAge=#{SAge}
if>
<if test="SBirthday!=null">
and SBirthday=#{SBirthday}
if>
<if test="ClazzNo!=null">
and ClazzNo=#{ClazzNo}
if>
where>
select>
mapper>
、
、
属于一整套标签,相当于switch-case语句,最多只会拼接其中的一条SQL语句。
语法格式
<choose>
<when test="条件判断语句1">
要拼接的SQL1
when>
<when test="条件判断语句2">
要拼接的SQL2
when>
<otherwise>
要拼接的SQL3
otherwise>
choose>
案例:传入一个实体类对象,按照SNo、SName、SBirthday、SAge、ClazzNo其中一个条件查找学生,当不传入条件时,返回SNo=1的学生对象。
selectStudentMapper.java
package org.example.StudentMapper;
import org.example.Entity.Student;
import java.util.List;
public interface selectStudentMapper {
/*
* 根据choose|when 按照SNo、SName、SBirthday、SAge、ClazzNo其中一个条件查找学生,当不传入条件时,返回SNo=1的学生对象.
* */
List<Student> getStudentByChooseCondition(Student student);
}
selectStudentMapper.xml
<mapper namespace="org.example.StudentMapper.selectStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<result property="ClazzNo" column="ClazzNo"/>
resultMap>
<select id="getStudentByChooseCondition" resultMap="StudentResultMap" parameterType="org.example.Entity.Student">
select * from student
<where>
<choose>
<when test="SNo!=null">
and SNo=#{SNo}
when>
<when test="SName!=null and SName!=''">
and SName=#{SName}
when>
<when test="SAge!=null">
and SAge=#{SAge}
when>
<when test="SBirthday!=null">
and SBirthday=#{SBirthday}
when>
<when test="ClazzNo!=null">
and ClazzNo=#{ClazzNo}
when>
<otherwise>
and SNo=1;
otherwise>
choose>
where>
select>
mapper>
MyBatisTest.java
import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.insertStudentMapper;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session= SqlSessionUtils.getSqlSession();
selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
Student student=new Student(null,null,18,null,null,null);
System.out.println(mapper.getStudentByChooseCondition(student));
}
}
一般用于去除sql语句中多余的and
关键字,逗号,或者给sql语句前拼接 where
、set
以及values(
等前缀,或者添加)
等后缀,可用于选择性插入、更新、删除或者条件查询等操作。
作用:当
中有返回语句时,进行字符拼接处理(无返回语句则不执行)。
常见属性:
属性 | 说明 |
---|---|
prefix | 给SQL语句拼接的前缀。 |
suffix | 给SQL语句拼接的后缀。 |
prefixOverrides | 去掉SQL语句前面指定的前缀。 |
suffixOverrides | 去掉SQL语句后面指定的后缀。 |
案例1:trim实现
功能,解决上文中提到的
存在的问题。
<mapper namespace="org.example.StudentMapper.selectStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<result property="ClazzNo" column="ClazzNo"/>
resultMap>
<select id="getStudentByIfCondition" resultMap="StudentResultMap" parameterType="org.example.Entity.Student">
select * from student
<trim prefix="where" prefixOverrides="and">
<if test="SNo!=null">
and SNo=#{SNo}
if>
<if test="SName!=null and SName !=''">
and SName=#{SName}
if>
<if test="SAge!=null">
and SAge=#{SAge}
if>
<if test="SBirthday!=null">
and SBirthday=#{SBirthday}
if>
<if test="ClazzNo!=null">
and ClazzNo=#{ClazzNo}
if>
trim>
select>
mapper>
案例2:去掉多于的逗号
若使用以下mapper映射文件:
<mapper namespace="org.example.StudentMapper.insertStudentMapper">
<insert id="insertByStudent" parameterType="org.example.Entity.Student" useGeneratedKeys="true" keyProperty="SNo">
insert into student (
<if test="SNo!=null">
SNo,
if>
<if test="SName!=null">
SName,
if>
<if test="SAge!=null">
SAge,
if>
<if test="SBirthday!=null">
SBirthday,
if>
<if test="ClazzNo!=null">
ClazzNo
if>
) VALUES (
<if test="SNo!=null">
#{SNo},
if>
<if test="SName!=null">
#{SName},
if>
<if test="SAge!=null">
#{SAge},
if>
<if test="SBirthday!=null">
#{SBirthday},
if>
<if test="ClazzNo!=null">
#{ClazzNo}
if>
)
insert>
mapper>
若最后的ClazzNo为空,则SQL变为:
insert into student (SNo,SName,SAge,SBirthday,) values(SNo,SName,SAge,SBirthday,)
显然有多于的逗号,故,需要改为:
<mapper namespace="org.example.StudentMapper.insertStudentMapper">
<insert id="insertByStudent" parameterType="org.example.Entity.Student" useGeneratedKeys="true" keyProperty="SNo">
insert into student
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="SNo!=null">
SNo,
if>
<if test="SName!=null">
SName,
if>
<if test="SAge!=null">
SAge,
if>
<if test="SBirthday!=null">
SBirthday,
if>
<if test="ClazzNo!=null">
ClazzNo
if>
trim>
<trim prefix="values(" suffix=")" suffixOverrides=",">
<if test="SNo!=null">
#{SNo},
if>
<if test="SName!=null">
#{SName},
if>
<if test="SAge!=null">
#{SAge},
if>
<if test="SBirthday!=null">
#{SBirthday},
if>
<if test="ClazzNo!=null">
#{ClazzNo}
if>
trim>
insert>
mapper>
foreach通常用来对集合的遍历,事实上,你可以传递一个 list
实例、 array
数组或map
实例作为参数对象传给 MyBatis。当你这么做的时候,MyBatis 会自动将它包装在一个 Map 中,用名称作为键。
属性 | 说明 |
---|---|
collection | 指明需要遍历的对象(list、array、map等),list、array对象的collection值默认为"list"与"array",而Map无默认的collection属性值。事实上,可在入参时使用@Param(“keyName”)注解指定collection属性值,此时"list"与"array"会失效。 |
item | 集合元素在被遍历时的别名。 |
index | list、array中是元素的索引,而map中是元素的key值。 |
open | 遍历集合时的开始符号,用于拼接SQL语句。 |
separator | 元素之间的分隔符,用于拼接SQL语句。 |
close | 遍历集合时的结束符号,用于拼接SQL语句。 |
selectStudentMapper.java
package org.example.StudentMapper;
import org.apache.ibatis.annotations.Param;
import org.example.Entity.Student;
import java.util.List;
public interface selectStudentMapper {
/*
* 传入多个Student对象,根据SNo的值,查找所有Student对象
* */
List<Student> getStudentBySNo(@Param("studentsList") List<Student> studentsList);
}
selectStudentMapper.xml
<mapper namespace="org.example.StudentMapper.selectStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<result property="ClazzNo" column="ClazzNo"/>
resultMap>
<select id="getStudentBySNo" parameterType="java.util.List" resultMap="StudentResultMap">
select * from student
<where>
<if test="studentsList!=null">
<foreach collection="studentsList" item="student" open="and SNo in(" separator="," close=")">
#{student.SNo}
foreach>
if>
where>
select>
mapper>
MyBatisTest.java
import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session= SqlSessionUtils.getSqlSession();
selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
Student student1=new Student(1,null,null,null,null,null);
Student student2=new Student(2,null,null,null,null,null);
List<Student>list=new ArrayList<>();
list.add(student1);
list.add(student2);
System.out.println(mapper.getStudentBySNo(list));
}
}
注意,这里传入的不是实体类数组。
selectStudentMapper.java
package org.example.StudentMapper;
import org.apache.ibatis.annotations.Param;
import org.example.Entity.Student;
import java.util.List;
public interface selectStudentMapper {
/*
* 传入SNo数组,查找所有Student对象
* */
List<Student> getStudentBySNo(@Param("studentsArray") Integer[] SNo);
}
selectStudentMapper.xml
<mapper namespace="org.example.StudentMapper.selectStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<result property="ClazzNo" column="ClazzNo"/>
resultMap>
<select id="getStudentBySNo" resultMap="StudentResultMap">
select * from student
<where>
<if test="studentsArray!=null">
<foreach collection="studentsArray" item="studentSNo" open="and SNo in(" separator="," close=")">
#{studentSNo}
foreach>
if>
where>
select>
mapper>
MyBatisTest.java
import org.apache.ibatis.session.SqlSession;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session= SqlSessionUtils.getSqlSession();
selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
Integer[] snoArray=new Integer[2];
snoArray[0]=1;
snoArray[1]=2;
System.out.println(mapper.getStudentBySNo(snoArray));
}
}
5.3.1 获取键值对
selectStudentMapper.java
package org.example.StudentMapper;
import org.apache.ibatis.annotations.Param;
import org.example.Entity.Student;
import java.util.List;
import java.util.Map;
public interface selectStudentMapper {
/*
* 根据学生姓名、年龄查找学生
* */
List<Student> getStudentBySNameAndSAge(@Param("StudentMap")Map<String,Integer>map);
}
selectStudentMapper.xml
<mapper namespace="org.example.StudentMapper.selectStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<result property="ClazzNo" column="ClazzNo"/>
resultMap>
<select id="getStudentBySNameAndSAge" resultMap="StudentResultMap">
select * from student
where
<if test="StudentMap!=null and StudentMap.size()>0">
(SName,SAge) in
<foreach collection="StudentMap.entrySet()" item="value" index="key" separator="," open="(" close=")">
(#{key},#{value})
foreach>
if>
select>
mapper>
MyBatisTest.java
import org.apache.ibatis.session.SqlSession;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
import java.util.HashMap;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session= SqlSessionUtils.getSqlSession();
selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
HashMap<String,Integer>map=new HashMap<>();
map.put("张三",18);
map.put("李四",19);
System.out.println(mapper.getStudentBySNameAndSAge(map));
}
}
5.3.2 获取键
selectStudentMapper.xml
<mapper namespace="org.example.StudentMapper.selectStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<result property="ClazzNo" column="ClazzNo"/>
resultMap>
<select id="getStudentBySNameAndSAge" resultMap="StudentResultMap">
select * from student
where
<if test="StudentMap!=null and StudentMap.size()>0">
(SName) in
<foreach collection="StudentMap.keySet()" item="key" separator="," open="(" close=")">
(#{key})
foreach>
if>
select>
mapper>
标签和
标签一样,用于动态生成,其用在update语句当中。
作用:在至少有一个子元素返回了SQL语句时,才会向SQL语句中添加SET,并且如果SET之后是以,开头的话,会自动将其删掉,相当于:
<trim prefix="SET" prefixOverrides=",">
...
trim>
在实际开发中会遇到许多相同的SQL,比如根据某个条件筛选,这个筛选很多地方都能用到,我们可以将其抽取出来成为一个公用的部分,这样修改也方便,一旦出现了错误,只需要改这一处便能处处生效了,此时就用到了
这个标签了。而
标签,用于引用
标签定义的常量。
例:
<sql id="selectAll">
select * from student
sql>
<select id="getStudentBySNo" resultMap="StudentResultMap">
<include refid="selectAll"/>
WHERE 1=1
<if test="SNo != null">
AND SNo like #{SNo}
if>
select>
对于MyBatis中的各种查询功能,有:
下面以Course实体类为例:
package org.example.Entity;
public class Course {
private Integer CNo;
private String CName;
/*其余是标准JavaBean内容*/
}
mybatis-config.xml
<mapper resource="CourseMapper/selectCourseMapper.xml"/>
selectCourseMapper.java
package org.example.CourseMapper;
import org.example.Entity.Course;
import java.util.List;
import java.util.Map;
public interface selectCourseMapper {
/*
* 获取包含所有实体类的集合
* */
List<Course> getAllCourse();
}
selectCourseMapper.xml
<mapper namespace="org.example.CourseMapper.selectCourseMapper">
<resultMap id="CourseResultMap" type="org.example.Entity.Course" autoMapping="false">
<id property="CNo" column="CNo"/>
<result property="CName" column="CName"/>
resultMap>
<select id="getAllCourse" resultMap="CourseResultMap">
select * from course;
select>
mapper>
mybatis-config.xml
<mapper resource="CourseMapper/selectCourseMapper.xml"/>
selectCourseMapper.java
package org.example.CourseMapper;
import org.example.Entity.Course;
import java.util.List;
public interface selectCourseMapper {
/*
* 根据CNo查询课程信息
* */
List<Course> getCourseByCNo(Integer CNo);
}
selectCourseMapper.xml
<mapper namespace="org.example.CourseMapper.selectCourseMapper">
<resultMap id="CourseResultMap" type="org.example.Entity.Course" autoMapping="false">
<id property="CNo" column="CNo"/>
<result property="CName" column="CName"/>
resultMap>
<select id="getCourseByCNo" resultMap="CourseResultMap">
select * from course where CNo=#{CNo}
select>
mapper>
mybatis-config.xml
<mapper resource="CourseMapper/selectCourseMapper.xml"/>
selectCourseMapper.java
package org.example.CourseMapper;
import org.example.Entity.Course;
import java.util.List;
public interface selectCourseMapper {
/*
* 查询课程总数
* */
Integer getCountOfCourse();
}
selectCourseMapper.xml
<mapper namespace="org.example.CourseMapper.selectCourseMapper">
<resultMap id="CourseResultMap" type="org.example.Entity.Course" autoMapping="false">
<id property="CNo" column="CNo"/>
<result property="CName" column="CName"/>
resultMap>
<select id="getCountOfCourse" resultType="java.lang.Integer">
select count(*) from course;
select>
mapper>
mybatis-config.xml
<mapper resource="CourseMapper/selectCourseMapper.xml"/>
selectCourseMapper.java
package org.example.CourseMapper;
import org.example.Entity.Course;
import java.util.List;
public interface selectCourseMapper {
/*
* 根据CNo获取课程信息的map集合
* */
Map<String,Object>getCourseByCNoToMap(Integer CNo);//使用Object作为值类型,便于类型转换.
}
selectCourseMapper.xml
<mapper namespace="org.example.CourseMapper.selectCourseMapper">
<resultMap id="CourseResultMap" type="org.example.Entity.Course" autoMapping="false">
<id property="CNo" column="CNo"/>
<result property="CName" column="CName"/>
resultMap>
<select id="getCourseByCNoToMap" resultType="java.util.Map">
select * from course where CNo=#{CNo}
select>
mapper>
MyBatisTest.java
import org.apache.ibatis.session.SqlSession;
import org.example.CourseMapper.selectCourseMapper;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.junit.Test;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session= SqlSessionUtils.getSqlSession();
selectCourseMapper mapper=session.getMapper(selectCourseMapper.class);
System.out.println(mapper.getCourseByCNoToMap(101));
}
}
mybatis-config.xml
<mapper resource="CourseMapper/selectCourseMapper.xml"/>
selectCourseMapper.java
package org.example.CourseMapper;
import org.example.Entity.Course;
import java.util.List;
import java.util.Map;
public interface selectCourseMapper {
/*
* 获取所有课程信息的map集合
* */
List<Map<String,Object>>getAllCourseToMap();
}
selectCourseMapper.xml
<mapper namespace="org.example.CourseMapper.selectCourseMapper">
<resultMap id="CourseResultMap" type="org.example.Entity.Course" autoMapping="false">
<id property="CNo" column="CNo"/>
<result property="CName" column="CName"/>
resultMap>
<select id="getAllCourseToMap" resultType="java.util.Map">
select * from course
select>
mapper>
MyBatisTest.java
import org.apache.ibatis.session.SqlSession;
import org.example.CourseMapper.selectCourseMapper;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.junit.Test;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session= SqlSessionUtils.getSqlSession();
selectCourseMapper mapper=session.getMapper(selectCourseMapper.class);
System.out.println(mapper.getAllCourseToMap());
}
}
注意,在模糊查询当中,不能使用#{}
,例:
<mapper namespace="org.example.mapper.SQLMapper">
<select id="getUserByLike" resultType="User">
select * from tb_user where username like '%#{username}%';
select>
mapper>
这是因为#{}
的实质是参数注入,而当其处于SQL语句的字符串当中时,?
就不会被注入参数,而是被当做字符串常量。
<mapper namespace="org.example.mapper.SQLMapper">
<select id="getUserByLike" resultType="User">
select * from tb_user where username like '%${username}%';
select>
mapper>
<mapper namespace="org.example.mapper.SQLMapper">
<select id="getUserByLike" resultType="User">
select * from tb_user where username like concat('%',#{username},'%');
select>
mapper>
<mapper namespace="org.example.mapper.SQLMapper">
<select id="getUserByLike" resultType="User">
select * from tb_user where username like '%'#{username}'%';
select>
mapper>
mybaits-config.xml
<mapper resource="StudentMapper/insertStudentMapper.xml"/>
insertStudentMapper.java
package org.example.StudentMapper;
import org.example.Entity.Student;
public interface insertStudentMapper {
/*
* 用实体类对象进行插入
* */
int insertByStudent(Student student);
}
insertStudentMapper.xml
<mapper namespace="org.example.StudentMapper.insertStudentMapper">
<insert id="insertByStudent" >
insert into student VALUES (#{SNo},#{SName}, #{SAge}, #{SBirthday},#{ClazzNo})
insert>
mapper>
MyBatisTest.java
import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.insertStudentMapper;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session= SqlSessionUtils.getSqlSession();
insertStudentMapper mapper=session.getMapper(insertStudentMapper.class);
String str="2004-03-24 23:38:47";
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date Birthday=simpleDateFormat.parse(str);
Student student=new Student(5,"田七",19,Birthday,301,null);
System.out.println(mapper.insertByStudent(student));
System.out.println(student.getSNo());
}
}
这里,是在传入实体类对象时就指定了SNo的值,事实上,由于SNo是自增主键,故可不指定,而是赋值为NULL,且,可设置以下两个属性,使得插入之后,传入的实体类对象本身的SNo也会被赋值:
insertStudentMapper.xml
<mapper namespace="org.example.StudentMapper.insertStudentMapper">
<insert id="insertByStudent" useGeneratedKeys="true" keyProperty="SNo">
insert into student VALUES (null,#{SName}, #{SAge}, #{SBirthday},#{ClazzNo})
insert>
mapper>
测试代码中的实体类SNo只需要赋值为null即可:
SqlSession session= SqlSessionUtils.getSqlSession();
insertStudentMapper mapper=session.getMapper(insertStudentMapper.class);
String str="2004-03-24 23:38:47";
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date Birthday=simpleDateFormat.parse(str);
Student student=new Student(null,"田七",19,Birthday,301,null);
mapper.insertByStudent(student)
insertStudentMapper.java
package org.example.StudentMapper;
import org.example.Entity.Student;
import java.util.List;
public interface insertStudentMapper {
/*
* 使用集合进行批量插入
* */
Integer insertByStudentList(List<Student> studentList);
}
insertStudentMapper.xml
<mapper namespace="org.example.StudentMapper.insertStudentMapper">
<insert id="insertByStudentList" useGeneratedKeys="true" keyProperty="SNo">
insert into student(SNo, SName, SAge, SBirthday, ClazzNo)
values
<foreach collection="studentList" separator="," item="student">
(null,#{student.SName},#{student.SAge},#{student.SBirthday},#{student.ClazzNo})
foreach>
insert>
mapper>
updateStudentMapper.java
package org.example.StudentMapper;
import org.example.Entity.Student;
public interface updateStudentMapper {
/*
* 根据SNo进行全字段修改
* */
Integer updateStudentBySNo(Student student);
}
updateStudentMapper.xml
<mapper namespace="org.example.StudentMapper.updateStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<result property="ClazzNo" column="ClazzNo"/>
resultMap>
<update id="updateStudentBySNo">
update student
<set>
SName=#{SNAme},SBirthday=#{SBirthday},SAge=#{SAge},ClazzNo=#{ClazzNo}
set>
where SNo=#{SNo}
update>
mapper>
updateStudentMapper.java
package org.example.StudentMapper;
import org.example.Entity.Student;
public interface updateStudentMapper {
/*
* 根据SNo进行动态修改
* */
Integer dynamicUpdateStudentBySNo(Student student);
}
updateStudentMapper.xml
<mapper namespace="org.example.StudentMapper.updateStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<result property="ClazzNo" column="ClazzNo"/>
resultMap>
<update id="updateStudentBySNo">
update student
<set>
<if test="SName!=null and SName!=''">
SName=#{SNAme},
if>
<if test="SBirthday!=null">
SBirthday=#{SBirthday},
if>
<if test="SAge!=null">
SAge=#{SAge},
if>
<if test="ClazzNo!=null">
ClazzNo=#{ClazzNo}
if>
set>
where SNo=#{SNo}
update>
mapper>
deleteStudentMapper.java
package org.example.StudentMapper;
public interface deleteStudentMapper {
/*
* 根据SNo删除Student对象
* */
Integer deleteStudentBySNo(Integer SNo);
}
deleteStudentMapper.xml
<mapper namespace="org.example.StudentMapper.deleteStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<result property="ClazzNo" column="ClazzNo"/>
resultMap>
<delete id="deleteStudentBySNo">
delete from student where SNo=#{SNo}
delete>
mapper>
deleteStudentMapper.java
package org.example.StudentMapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface deleteStudentMapper {
/*
* 根据SNo列表进行批量删除
* */
Integer deleteStudentBySNoList(@Param("SNoList") List<Integer> SNoList);
}
deleteStudentMapper.xml
<mapper namespace="org.example.StudentMapper.deleteStudentMapper">
<resultMap id="StudentResultMap" type="org.example.Entity.Student" autoMapping="false">
<id property="SNo" column="SNo"/>
<result property="SName" column="SName"/>
<result property="SBirthday" column="SBirthday"/>
<result property="SAge" column="SAge"/>
<result property="ClazzNo" column="ClazzNo"/>
resultMap>
<delete id="deleteStudentBySNoList">
delete from student where SNo in (
<foreach collection="SNoList" item="SNo" separator=",">
#{SNo}
foreach>
)
delete>
mapper>
所有的查询都要连接数据库,而连接数据库需要耗费资源,如果能将一次查询的结果暂存到一个可以直接取到的地方,使得再次查询相同数据时直接走缓存就不用走数据库了,这就是缓存(Cache)(注,缓存只针对查,而不针对增删改)。即,缓存数据,是存在内存中的临时数据,将用户经常查询的数据放在缓存(内存)中,用户去查询数据时就不用从磁盘上查询,而是从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题,且,缓存的数据一般是经常查询并且不经常改变的数据。
MyBatis包含一个非常强大的查询缓存特性,可以非常方便地定制和配置缓存,并极大提高查询效率。其默认定义了两级缓存:一级缓存和二级缓存。
MyBatis缓存查询的顺序:
一级缓存是默认开启的,其范围是SqlSession级别,当使用SqlSession查询数据时,如果下一次再使用相同的SqlSession进行查询时,就会直接从缓存中取数据,如果缓存中没有相应数据才从数据库中取数据。
例:
MyBatisTest.java
import org.apache.ibatis.session.SqlSession;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session= SqlSessionUtils.getSqlSession();
selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
System.out.println(mapper.getStudentById(1));
System.out.println(mapper.getStudentById(1));
}
}
使一级缓存失效的四种情况:
&emps;此时会建立两次连接。
import org.apache.ibatis.session.SqlSession;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session1= SqlSessionUtils.getSqlSession();
selectStudentMapper mapper1=session1.getMapper(selectStudentMapper.class);
SqlSession session2= SqlSessionUtils.getSqlSession();
selectStudentMapper mapper2=session1.getMapper(selectStudentMapper.class);
System.out.println(mapper1.getStudentById(1));
System.out.println(mapper2.getStudentById(1));
}
}
此时会两次进入数据库查询。
import org.apache.ibatis.session.SqlSession;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session=SqlSessionUtils.getSqlSession();
selectStudentMapper mapper=session.getMapper(selectStudentMapper.class);
System.out.println(mapper.getStudentById(1));
System.out.println(mapper.getStudentById(2));
}
}
import org.apache.ibatis.session.SqlSession;
import org.example.Entity.Student;
import org.example.MyBatisUtils.SqlSessionUtils;
import org.example.StudentMapper.insertStudentMapper;
import org.example.StudentMapper.selectStudentMapper;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyBatisTest {
@Test
public void test() throws Exception{
SqlSession session=SqlSessionUtils.getSqlSession();
selectStudentMapper selectStudentMapper=session.getMapper(selectStudentMapper.class);
insertStudentMapper insertStudentMapper=session.getMapper(org.example.StudentMapper.insertStudentMapper.class);
String str="2004-03-24 23:38:47";
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date Birthday=simpleDateFormat.parse(str);
System.out.println(selectStudentMapper.getStudentById(1));
insertStudentMapper.insertByStudent(new Student(null,"田七",20,Birthday,301,null));
System.out.println(selectStudentMapper.getStudentById(2));
}
}
SqlSession session=SqlSessionUtils.getSqlSession();
session.clearCache();
一级缓存作用域是sqlsession
级别的,同一个sqlsession
中执行相同的sql查询(相同的sql和参数),第一次会去查询数据库并写到缓存中,第二次从一级缓存中取。如果中间sqlSession
去执行commit
操作(执行插入、更新、删除),则会清空SqlSession
中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
一级缓存时执行commit
,close
,增删改等操作,就会清空当前的一级缓存;当对SqlSession
执行更新操作(update
、delete
、insert
)后并执行commit
时,不仅清空其自身的一级缓存(执行更新操作的效果),也清空二级缓存(执行commit()的效果)。
事实上,MyBatis在开启一个数据库会话时,会创建一个新的SqlSession
对象,SqlSession
对象中会有一个Executor
对象,Executor
对象中持有一个PerpetualCache
对象,见下面代码。当会话结束时,SqlSession
对象及其内部的Executor对象还有PerpetualCache
对象也一并释放掉。
二级缓存是SqlSessionFactory级别的,通过同一个SqlSessionFactory创建的SqlSession查询的结果是会被缓存的,而过再次执行相同的查询语句,结果就会从缓冲中获取,且,二级缓存需要手动开启。
注:二级缓存只有在一级缓存死掉才可以执行。
<settings>
<setting name="cacheEnabled" value="true"/>
settings>
在mapper.xml(sql映射文件中)的mapper标签中添加
标签。
<select id="getStudentByClazzNo" resultMap="StudentResultMap" useCache="true">
select * from student where student.ClazzNo=#{ClazzNo}
select>
两次查询之间执行了任意的增删改,会使一、二级缓存同时失效。
在mapper配置文件中添加的cache标签可以设置一些属性:
正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表,Hibernate支持正向工程。
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成以下资源:
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-maven-pluginartifactId>
<version>1.3.7version>
<configuration>
<verbose>trueverbose>
<overwrite>trueoverwrite>
configuration>
plugin>
plugins>
build>
配置文件模板:
<generatorConfiguration>
<properties resource="properties/xx.properties">properties>
<classPathEntry location="C:\Users\Vergi\.m2\repository\mysql\mysql-connector-java\8.0.11\mysql-connector-java-8.0.11.jar" />
<context id="default" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressDate" value="true" />
<property name="suppressAllComments" value="true" />
commentGenerator>
<jdbcConnection driverClass="${db.driver}"
connectionURL="${db.url}"
userId="${db.user}"
password="${db.password}">
jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
javaTypeResolver>
<javaModelGenerator targetPackage="mybatis.generator.model"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="false" />
<property name="trimStrings" value="true" />
javaModelGenerator>
<sqlMapGenerator targetPackage="mybatis.generator.mappers"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="false" />
sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER"
targetPackage="mybatis.generator.dao" targetProject=".\src\main\java">
<property name="enableSubPackages" value="false" />
javaClientGenerator>
<table tableName="student"/>
<table tableName="product"/>
context>
generatorConfiguration>
留个小坑,太忙了,先推进度,有空再写。