目录
前言
1. MyBatis
2. 示例(使用MyBatis访问数据库)
核心配置文件
映射配置文件
1. MyBatis注解
2. 级联查询
3. 动态SQL(减少代码工作量)
4. 分页
5. 缓存
6. 存储过程
7. (根据数据库表)自动生成:POJO类、mapper映射文件、mapper 接口
8. Maven+Spring+MyBatis
一个轻量级访问数据库的JavaORM开源框架(对JDBC进一步封装),专注于SQL语句(使用xml文件或注解 将SQL和代码分离)。
前言
MyBatis目录
- MyBatis
MyBatis目录
1. lib目录
MyBatis依赖的jar包
2. mybatis-3.5.11.jar
MyBatis核心jar包
3. mybatis-3.5.11.pdf
MyBatis使用说明英文文档
序号 | MyBatis依赖的jar包 | 描述 |
---|---|---|
1 | asm-7.1.jar | 操作Java字节码的类库 |
2 | cglib-3.3.0.jar | 用来动态继承Java类或实现接口 |
3 | commons-logging-1.2.jar | 用于通用日志处理 |
4 | javassist-3.29.2-GA.jar | 分析、编码和创建Java类库 |
5 | log4j-api-2.19.0.jar | 日志系统 |
6 | ognl-3.3.4.jar | OGNL的类库 |
7 | reload4j-1.2.22.jar | |
8 | slf4j-api-2.0.1.jar | 日志系统的封装,对外提供统一的API接口 |
优点
1. 简化JDBC开发(几乎所有的JDBC代码)。
2. 最简单的持久化框架(灵活、易学)。
3. SQL写在xml文件中,和代码分离,降低了耦合度,便于统一管理,提高了代码的可重用性。
4. 支持编写动态SQL语句。
5. 半自动ORM框架(只能自动将表映射到Java对象:读,不能自动将Java对象映射到表:存)
6. 支持以存储过程的形式封装SQL。将业务逻辑保留在数据库之外,增强应用的可移植性,更易于部署和测试。
缺点
1. 编写SQL语句的工作量较大。
2. SQL语句依赖于数据库(数据库移植性差:不能随意更换数据库)。
MyBatis和Hibernate的区别
1. Hibernate
1. 全自动ORM框架。
2. 使用HQL语句,独立于数据库(数据库移植性高)。不需要编写大量的SQL就可以完成映射,但会多消耗性能,且开发员不能自主的进行SQL性能优化。级联比MyBatis强大。Hibernate的DAO层开发比MyBatis简单,Mybatis使用注解方式时需要维护SQL和结果映射。
3. 更好的二级缓存(如果出现脏数据,系统会报出错误并提示);可集成第三方缓存。在核心配置文件配置缓存提供者;每个映射配置文件中配置并发策略。
2. MyBatis
1. 半自动ORM框架
使用xml或注解手动配置SQL映射(将Java类属性映射到SQL字符串中对应的表字段)。
对于查询结果:会自动将字段映射到对应的Java类属性。
2. 需手动编写SQL(灵活多变),依赖于数据库(数据库移植性差)。支持动态SQL处理列表、动态生成表名、支持存储过程。工作量相对较大。可以进行更为细致的SQL优化,可以减少不必要的SQL语句执行(提高性能)。
3. 使用二级缓存时需特别小心(如果不能完全确定数据更新操作的波及范围,应避免Cache的盲目使用,否则脏数据的出现会给系统的正常运行带来很大的隐患)。在核心配置文件配置开启二级缓存;每个映射配置文件中配置多个的缓存机制,针对不同的表可使用不同的缓存机制。Mybatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现。
综上所述
1. Hibernate适合需求明确、业务固定的项目(如:OA项目、ERP项目、CRM项目等)。
2. MyBatis适合需求多变、性能要求苛刻的互联网项目(如:电商项目、金融类型、旅游类、售票类项目等)。
如果使用的是Maven项目时,只需在pom.xml文件中添加如下依赖内容(不需要手动导入jar包):
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
</dependencies>
- 示例(使用MyBatis访问数据库)
- 创建Web应用
1. 导入依赖包
将mysql-connector-java-xxx.jar、mybatis-xxx.jar、MyBatis的lib目录下的所有依赖jar复制到/WEB-INF/lib目录中。
2. 创建log4j.properties日志文件(在src目录下),内容如下:
# 全局的日志配置
log4j.rootLogger=ERROR,stdout
# MyBatis的日志配置(将com.sst.cx包下所有类的日志记录级别设置为DEBUG)
log4j.logger.com.sst.cx=DEBUG
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
3. 创建表(在数据库中)
create table EMPLOYEE (
id INT NOT NULL auto_increment,
first_name VARCHAR(20) default NULL,
last_name VARCHAR(20) default NULL,
salary INT default NULL,
PRIMARY KEY (id)
);
- 创建持久化类
===》Employee.java(在com.sst.cx.domain包下创建)遵循POJO规范,属性名和表字段名通常需一致。
不一致则需进行额外配置
方式1(在核心配置文件中配置)
如果属性名为驼峰写法,数据库字段名为下划线写法时可使用该方式。
<settings>
<!-- 全局处理属性名和表字段名不一致(驼峰转下划线) -->
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
方式2(在sql中定义别名)
select first_name firstName from EMPLOYEE
方式3(通过resultMap元素来映射字段名和类属性名)
<select id="selectAllEmployee" resultMap="myResult">
select * from EMPLOYEE
</select>
<resultMap type="com.sst.cx.domain.Employee" id="myResult">
<id property="id" column="id"/>
<result property="firstName" column="first_name"/>
</resultMap>
package com.sst.cx.domain;
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String firstName ) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName( String lastName ) {
this.lastName = lastName;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
// System.out.println(employeeTmp);会输出该方法的返回值。
public String toString() {
return "工号为"+id+"的员工:"+firstName+lastName+"的工资为$"+salary;
}
}
- 创建映射配置文件
===》EmployeeMapper.xml(在com.sst.cx.mappper包下创建,一般命名为类名+Mapper)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sst.cx.mapper.EmployeeMapper">
<!-- 添加一个员工 -->
<insert id="addEmployee" parameterType="com.sst.cx.domain.Employee">
insert into EMPLOYEE(first_name,last_name,salary) values(#{firstName},#{lastName},#{salary})
</insert>
<!-- 查询所有员工信息 -->
<select id="selectAllEmployee" resultType="com.sst.cx.domain.Employee">
select * from EMPLOYEE
</select>
<!-- 删除指定员工 -->
<delete id="deleteEmployee">
delete from EMPLOYEE where id=#{id}
</delete>
<!-- 更新指定员工 -->
<update id="updateEmployee">
update EMPLOYEE set salary=${salary} where id=#{id}
</update>
</mapper>
说明:
1. mapper元素:映射配置文件的根元素
namespace属性:用于指定唯一的命名空间(一般为:包名+映射文件名)。
2. insert元素(对应一个插入语句)、delete元素(对应一个删除语句)、update元素(对应一个更新语句)、select元素(对应一个查询语句)。
1. id属性:通过namespace属性+id属性就可以找到对应的SQL。
2. parameterType属性:参数类型(给占位符赋值),可选(Mybatis会自动推断)。
3. resultType属性:查询结果类型(类的完全限定名),会自动将数据库中查询到的表数据映射到Java对象中。
3. #{} 表示一个占位符,相当于?。#{xxx}表示使用xxx属性值为占位符赋值。
- 创建核心配置文件
===》mybatis-config.xml(在src目录下创建,一般命名为mybatis-config)
定义了:数据库连接相关参数(通常会将这些参数放置在单独的properties文件中,再使用properties元素引入。使用${参数名}来引用properties文件中的数据库连接参数值。)、映射文件的路径
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 全局处理属性名和表字段名不一致(驼峰转下划线) -->
<setting name="mapUnderscoreToCamelCase" value="true" />
<setting name="logImpl" value="LOG4J" />
</settings>
<!-- 配置mybatis运行环境 -->
<environments default="development">
<environment id="development">
<!-- 使用JDBC的事务管理 -->
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<!-- MySQL数据库驱动 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<!-- 连接数据库的URL -->
<property name="url"
value="jdbc:mysql://localhost:3306/Test?characterEncoding=utf8" />
<property name="username" value="root" />
<property name="password" value="12345678" />
</dataSource>
</environment>
</environments>
<!-- 添加映射文件,一个映射文件对应一个mapper元素 -->
<mappers>
<mapper resource="com/sst/cx/mapper/EmployeeMapper.xml" />
</mappers>
</configuration>
- 创建测试文件
===》Test.java(在com.sst.cx.test包下创建)
package com.sst.cx.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
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 com.sst.cx.domain.Employee;
public class Test {
public static void main(String[] args) throws IOException {
/*
1. SqlSessionFactoryBuilder对象
负责读取核心配置文件内容并保存到创建的SqlSessionFactory对象中(后就会被销毁,适合作为局部变量)。
提供了多个build方法重载:
build(InputStream);
build(InputStream,String);
build(InputStream,String,Properties);
build(InputStream,Properties);
build(Reader);
build(Reader,String);
build(Reader,String,Properties);
build(Reader,Properties);
build(Configuration);
通过源码可知,以上build方法都在调用build(Reader reader, String environment, Properties properties)方法。
可以发现配置信息可以通过3种方式传入:InputStream(字节流)、Reader(字符流)、Configuration(类)。字节流和字符流都属于读取xml配置文件的方式,又可分为2种方式:读取xml配置文件(常用)和编写代码。
*/
/*
2. SqlSessionFactory对象(对应一个数据库)
由SqlSessionFactoryBuilder配置对象创建。每次访问数据库时,负责创建SqlSession对象。
1. 程序运行期间只需创建一次(对应application作用域)。
看一下SqlSessionFactory接口的定义:
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
*/
// Reader configReader = Resources.getResourceAsReader(mybatis-config.xml");
// SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(configReader);
// 读取核心配置文件并创建SqlSessionFactory对象
InputStream configInputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(configInputStream);
/*
3. SqlSession对象(对应一次数据库连接)
用于执行映射文件中的SQL来持久化类(增删改查)。 类似于JDBC的Connection、Hibernate的Session。
1. 非线程安全(应该避免多个线程共享同一个SqlSession实例,使用完后应及时关闭)。
2. 每次访问数据库都需重新创建一个SqlSession对象(对应request作用域)。
3. 执行SQL的2种方式:
方式1. 创建映射器,让映射器通过命名空间和方法名来执行对应的SQL。
方式2. 直接通过命名空间+id来执行对应的SQL。
常用方法
void clearCache();
Configuration getConfiguration();
void rollback(boolean force);
void commit(boolean force);
int delete(String statement, Object parameter);
*/
// 创建SqlSession对象(对应一次数据库访问)
SqlSession sqlSession = ssf.openSession();
// 使用SqlSession对象执行映射文件中定义的SQL,并返回映射结果
// 添加员工
Employee employee = new Employee();
employee.setFirstName("张");
employee.setLastName("三");
employee.setSalary(100000);
sqlSession.insert("com.sst.cx.mapper.Employee.addEmployee", employee);
// 查询所有员工
List<Employee> employeeList = sqlSession.selectList("com.sst.cx.mapper.Employee.selectAllEmployee");
for (Employee employeeTmp : employeeList) {
System.out.println(employeeTmp);
// 删:sqlSession.delete("com.sst.cx.mapper.EmployeeMapper.deleteEmployee", employeeTmp);
// 改:sqlSession.update("com.sst.cx.mapper.EmployeeMapper.deleteEmployee", 修改后的employeeTmp);
}
// 提交事务
sqlSession.commit();
// 关闭SqlSession
sqlSession.close();
}
}
运行结果
核心配置文件
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 元素需按一下顺序配置,否则编译错误 -->
<configuration><!-- 核心配置文件的根元素 -->
<properties /><!-- 引入外部文件;设置name-value(可在其他地方引用) -->
<settings /><!-- 配置MyBatis的运行时行为 -->
<typeAliases /><!-- 取别名(替代类的完全限定名)可在映射文件中引用别名 -->
<typeHandlers /><!-- 类型处理器(负责类型转换) -->
<objectFactory /><!-- 对象工厂 -->
<plugins /><!-- 插件 -->
<environments><!-- 用于配置多套运行环境(开发、生产、测试) -->
<environment><!-- 对应一套运行环境 -->
<transactionManager /><!-- 指定事务管理器 -->
<dataSource /><!-- 配置数据库连接参数 -->
</environment>
</environments>
<databaseIdProvider /><!-- 数据库厂商标识 -->
<mappers /><!-- 映射器(所有映射文件的路径) -->
</configuration>
1. properties元素(定义的内容可以在其他元素中使用${name}来引用)
1. 引入外部properties文件(可以将数据库连接参数放置在database.properties文件中,然后引入该文件)。
<properties resource="database.properties"/>
2. 在property子元素中设置name-value。
<properties>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
2. settings元素
用于配置MyBatis的运行时行为,大多数使用默认值即可。
全配置的样例:
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
3. typeAliases元素
定义别名(在映射文件中不需再使用类的全限定名)
方式1:
<typeAliases>
<typeAlias alias = "Employee" type = "com.sst.cx.domain.Employee"/>
</typeAliases>
方式2:(对包路径下的所有类定义别名:去除包路径后的类名):
<typeAliases>
<package name = "com.sst.cx.domain"/>
</typeAliases>
4. typeHandlers元素(有多个typeHandler子元素)
对数据库类型和Java类型进行转换。
1. jdbcType属性:指定数据库类型。
2. javaType属性:指定对应的Java类型。
对于自定义类型,需要实现TypeHandler接口或继承BaseTypeHandle类。
5. environments元素
用于配置多套运行环境(一套环境对应一个environment子元素)。开发-生产环境分别使用不同的数据库。
default属性:默认使用哪一套环境,对应environment元素的运行环境id。
有2个子元素:transactionManager元素、dataSource元素。
例:
<environments default="development">
<environment id="development">
</environment>
</environments>
transactionManager元素
指定事务管理器(2种)
1. JDBC
应用服务器负责事务管理操作(如:提交、回滚)。
2. MANAGED
应用服务器负责管理连接生命周期。
例:
<!-- 使用JDBC的事务管理 -->
<transactionManager type="JDBC" />
dataSource元素
用于配置数据库连接相关参数
type属性:指定数据源的类型(3种)。
1. UNPOOLED
没有数据库连接池(效率低)
2. POOLED
MyBatis维护一个数据库连接池。对于每个数据库的操作,MyBatis都会使用连接池中的连接,并在操作完成后将它们返回到池中。(减少了创建新连接所需的初始连接和身份验证时间)。
3. JNDI
MyBatis将从JNDI 数据源中获取连接。
例:
<dataSource type="POOLED">
<!-- JDBC驱动程序类(数据库驱动) -->
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<!-- 要连接的数据库URL -->
<property name="url" value="jdbc:mysql://localhost:3306/Test?characterEncoding=utf8" />
<!-- 连接数据库使用的用户名 -->
<property name="username" value="root" />
<!-- 连接数据库使用的密码 -->
<property name="password" value="12345678" />
</dataSource>
6. mappers元素(有多个mapper子元素)
用于指定所有映射配置文件的路径。
例:
<!-- 添加映射文件,一个映射文件对应一个mapper元素 -->
<mappers>
<mapper resource="com/sst/cx/mapper/EmployeeMapper.xml" />
</mappers>
setting配置项 | 作用 | 配置选项 | 默认值 |
---|---|---|---|
cacheEnabled | 是否开启二级缓存 | true/false | true |
lazyLoadingEnabled | 是否开启延迟加载。开启后所有关联对象都会延迟加载。可在特定关联关系中设置fetchType属性进行覆盖 | true/false | false |
aggressiveLazyLoading | 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载 | true/false | 版本3.4.1 (不包含)之前的默认值为true,之后为false。 |
multipleResultSetsEnabled | 是否允许单一语句返回多结果集(需要兼容驱动) | true/false | true |
useColumnLabel | 使用列标签代替列名。不同的驱动会有不同的表现 | true/false | true |
useGeneratedKeys | 是否自动生成主键(需要驱动兼容。尽管一些驱动不能兼容但仍可正常工作,如:Derby) | true/false | false |
autoMappingBehavior | 指定如何自动映射列字段到属性。NONE表示取消自动映射;PARTIAL表示自动映射(不包括结果集);FULL表示自动映射(包括结果集,无论是否嵌套) | NONE、PARTIAL、FULL | PARTIAL |
autoMappingUnkno wnColumnBehavior | 指定自动映射当中未知列(或未知属性类型)时的行为。 默认为NONE:不处理;WARNING:只有当日志级别达到 WARN级别或者以下才会显示相关日志;FAILING:如果处理失败会抛出SqlSessionException异常 | NONE、WARNING、FAILING | NONE |
defaultExecutorType | 配置默认的执行器。SIMPLE:普通的执行器;REUSE:会重用预处理语句;BATCH:执行器将重用语句并执行批量更新 | SIMPLE、REUSE、BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间(决定驱动等待数据库响应的秒数) | 正整数 | Not Set (null) |
defaultFetchSize | 设置数据库驱动程序默认返回的条数限制 | 正整数 | Not Set (null) |
safeRowBoundsEnabled | 允许在嵌套语句中使用分页(RowBounds),如果允许,设置false | true/false | false |
safeResultHandlerEnabled | 允许在嵌套语句中使用分页(ResultHandler)。如果允许,设置false | true/false | true |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则映射 | true/false | false |
localCacheScope | MyBatis利用本地缓存机制防止循环引用和加速联复嵌套査询。默认值为SESSION(缓存一个会话中执行的所有查询)。若设置值为STATEMENT,本地会话仅用在语句执行上,对相同SqlScssion的不同调用将不会共享数据 | SESSION/STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数提供特定的JDBC类型时,为空值指定 JDBC 类型。某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER | NULL、VARCHAR、OTHER | OTHER |
lazyLoadTriggerMethods | 指定哪个对象的方法触发一次延迟加载 | — | equals、clone、hashCode、toString |
defaultScriptingLanguage | 指定动态 SQL 生成的默认语言 | — | org.apache.ibatis.script.ing.xmltags.XMLDynamicLanguageDriver |
callSettersOnNulls | 指定当结果集中值为 null 时,是否调用映射对象的 setter(map 对象时为 put)方法,这对于 Map.kcySet() 依赖或 null 值初始化时是有用的。注意,基本类型(int、boolean 等)不能设置成 null | true/false | false |
logPrefix | 指定 MyBatis 增加到日志名称的前缀 | 任何字符串 | Not set |
loglmpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动査找 | SLF4J/LOG4J/LOG4J2/JDK_LOGGING/COMMONS_LOGGING/ST DOUT_LOGGING/NO_LOGGING | Not set |
proxyFactory | 指定MyBatis创建具有延迟加栽能力的对象所用到的代理工具 | CGLIB/JAVASSIST | JAVASSIST(MyBatis3.3及以上) |
vfsImpl | 指定VFS的实现类 | 提供VFS类的全限定名(多个时以逗号分隔) | Not set |
useActualParamName | 允许用方法参数中声明的实际名称引用参数。要使用此功能,项目必须被编译为 Java 8 参数的选择。(从版本 3.4.1 开始可以使用) | true/false | true |
映射配置文件
映射器
用于:配置缓存、定义SQL/动态SQL语句、定义参数类型、定义查询结果/映射。
由以下2部分组成:
1. Java接口(可省略)
一个方法对应xml文件中的一个SQL语句。
2. xml映射配置文件 或 注解
文件中包含一组SQL语句(增删改查),这些语句称为映射语句或映射SQL语句。
如果同时使用xml方式和注解方式,xml会覆盖注解。
方式1. xml文件(常用)
SQL语句比较复杂(表关联、多个查询条件、级联)或存在动态SQL时,此方式相比注解方式:可读性和可维护性高,避免了重复编写SQL语句。
mapper元素的namespace属性用来定义命名空间,需要和接口的完全限定名一致。
方式2. 注解
在Java接口中添加注解(注入SQL)
例1(xml文件方式):
===》1. Java接口类(可省略)
package com.sst.cx.mapper;
import java.util.List;
import com.sst.cx.domain.Employee;
public interface EmployeeMapper {
// 添加一个员工
public void addEmployee(Employee employee);
// 查询所有员工信息
public List<Employee> selectAllEmployee();
}
===》2. xml映射配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sst.cx.mapper.EmployeeMapper">
<!-- 添加一个员工 -->
<insert id="addEmployee" parameterType="com.sst.cx.domain.Employee">
insert into EMPLOYEE(first_name,last_name,salary) values(#{firstName},#{lastName},#{salary})
</insert>
<!-- 查询所有员工信息 -->
<select id="selectAllEmployee" resultType="com.sst.cx.domain.Employee">
select * from EMPLOYEE
</select>
</mapper>
===》3. 在核心配置文件中添加该映射文件
<mappers>
<!-- MyBatis会读取该映射文件并 生成映射器 -->
<mapper resource="com/sst/cx/mapper/EmployeeMapper.xml" />
</mappers>
===》4. 测试
执行SQL(2种方式)
/*
方式1(通过SqlSession对象执行SQL)
1. selectOne
只能查询1条数据,查询到多条数据时会导致运行时错误。必须指定查询条件。
sqlSession.selectOne(String arg0, Object arg1)
2. selectList
sqlSession.selectList(String arg0)
sqlSession.selectList(String arg0, Object arg1)
第一个参数:由 命名空间+SQL id 组成,唯一对应一个SQL语句。
第二个参数:查询条件。
sqlSession.insert("com.sst.cx.mapper.Employee.addEmployee", employee);
List<Employee> employeeList = sqlSession.selectList("com.sst.cx.mapper.Employee.selectAllEmployee");
*/
// 方式2(通过SqlSession对象获取Mapper接口并调用其方法执行SQL)
// 推荐(可读性高;更符合面向对象思想,体现了业务逻辑;IDE会提供示错误提示和校验),这时Java接口不能省略(必须创建)。
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
employeeMapper.addEmployee(employee);
List<Employee> employeeList = employeeMapper.selectAllEmployee();
for (Employee employeeTmp : employeeList) {
System.out.println(employeeTmp);
}
例2(注解方式):
===》1. Java接口类
package com.sst.cx.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import com.sst.cx.domain.Employee;
@Mapper
public interface EmployeeMapper {
// 添加一个员工
@Insert(value = "insert into EMPLOYEE(first_name,last_name,salary) values(#{firstName},#{lastName},#{salary})")
public void addEmployee(Employee employee);
// 查询所有员工信息
@Select(value = "select * from EMPLOYEE")
public List<Employee> selectAllEmployee();
}
// 也可以在核心配置文件中添加<mapper class="com.sst.cx.mapper.EmployeeMapper"/>
// 也可以在Configuration对象上注册该接口configuration.addMapper(EmployeeMapper.class);
在核心配置文件的mappers元素中添加<package name="com.sst.cx.mapper"/>对包路径下所有Mapper进行统一配置。
===》2. 测试(测试代码同上)
映射配置文件的元素 | 描述 |
---|---|
mapper元素 | 映射文件的根节点。有一个namescape属性(1. 定义命名空间,用于区分不同的mapper,全局唯一;2. 当绑定DAO接口时,可以不用创建该接口的实现类,MyBatis会通过接口的完整限定名查找到对应的mapper配置来执行SQL语句)。 |
select元素 | 对应一个查询语句(可以自定义参数,返回结果集) |
insert元素 | 对应一个插入语句(执行后返回一个整数,代表插入的条数) |
update元素 | 对应一个更新语句(执行后返回一个整数,代表更新的条数) |
delete元素 | 对应一个删除语句(执行后返回一个整数,代表删除的条数) |
parameterMap元素 | 定义参数映射关系(即将被删除的元素,不再建议使用) |
sql元素 | 允许先定义一部分SQL(然后在多个SQL语句中使用)。 |
resultMap元素 | 用来描述数据库结果集与对象的对应关系(最复杂、最强大的元素,提供映射规则) |
cache元素 | 配置指定命名空间的缓存 |
cache-ref元素 | 引用其它命名空间缓存配置 |
1. select元素(最常用、最强大)
对应一个查询语句。
1. id属性:通过mapper元素的namespace属性+select元素的id属性就可以找到唯一对应的SQL。
2. resultType属性:指定结果集映射的Java类型。
3. parameterType属性:指定查询参数(int、float、String、JavaBean、Map等)。
例
定义一个id为selectAllEmployee的查询语句(参数类型为string,返回结果类型为Employee)
<select id="selectAllEmployee" resultType="com.sst.cx.domain.Employee" parameterType="string">
select * from EMPLOYEE WHERE first_name=#{firstName}
</select>
MyBatis会自动将以上配置转为以下SQL代码
String sql = "select * from EMPLOYEE WHERE first_name=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1,firstName);
传递多个参数
方式1. 使用Map传递参数(可读性差、不利于后期维护)不推荐
1. 映射文件
<select id="selectEmployeeByMap" resultType="com.sst.cx.domain.Employee" parameterType="map">
select * from EMPLOYEE WHERE first_name=#{firstName} AND last_name=#{lastName}
</select>
2. Java接口
public List<Employee> selectEmployeeByMap(Map<String, String> params);
3. 测试
Map<String,String> paramsMap = new HashMap<String,String>();
paramsMap.put("firstName","张");
paramsMap.put("lastName","三");
employeeMapper.selectEmployeeByMap(paramsMap);
方式2. 使用注解传递参数(当参数个数≤5时,更直观)
1. 映射文件
<select id="selectEmployeeByAn" resultType="com.sst.cx.domain.Employee">
select * from EMPLOYEE WHERE first_name=#{firstName} AND last_name=#{lastName}
</select>
2. Java接口
public List<Employee> selectEmployeeByAn(@Param("firstName") String name, @Param("lastName") String url);
方式3. 使用JavaBean传递参数(当参数个数>5时,建议该方式)
1. 映射文件
<select id="selectEmployeeByBean" resultType="com.sst.cx.domain.Employee">
select * from EMPLOYEE WHERE first_name=#{firstName} AND last_name=#{lastName}
</select>
2. Java接口
public List<Employee> selectEmployeeByBean(Employee employee);
select元素的常用属性 | 描述 |
---|---|
id | 通过mapper元素的namespace属性+select元素的id属性就可以找到唯一对应的SQL(不唯一会抛异常)。 |
parameterType | 指定传入SQL语句的参数类型的完全限定名或别名。是一个可选属性,MyBatis能自动推断出参数类型。支持基本数据类型(int、float、String等)和等复杂数据类型(JavaBean、Map)。 |
resultType | SQL语句执行后返回的结果类型(全限定名或者别名)。如果是集合类型,则返回的是集合元素的类型,可以使用resultType或resultMap。 |
resultMap | 映射集的引用,与<resultMap>元素(MyBatis最复杂的元素,可以配置映射规则、级联、typeHandler 等)一起使用,可以使用 resultType或resultMap。 |
flushCache | SQL语句执行后是否清空之前查询的本地缓存和二级缓存,默认值为false。 |
useCache | 是否开启二级缓存,默认值为true(会将査询结果存入二级缓存中)。 |
timeout | 设置执行该操作的超时时间(以s单位),超时将抛出异常。 |
fetchSize | 获取记录的总条数设定,默认值是数据库厂商提供的JDBC驱动所设置的条数。 |
statementType | 执行SQL时使用的Statement类型,取值为STATEMENT(Statement)、 PREPARED(PreparedStatement,默认)、CALLABLE(CallableStatement) 。 |
resultSetType | 针对JDBC的ResultSet接口而言,取值为FORWARD_ONLY(只允许向前访问)、SCROLL_SENSITIVE(双向滚动,但不及时更新)、SCROLLJNSENSITIVE(双向滚动,及时更新)。 |
2. insert元素
对应一个插入语句。
例
定义一个id为addEmployee的插入语句(参数类型为Employee)
<insert id="addEmployee" parameterType="com.sst.cx.domain.Employee">
insert into EMPLOYEE(first_name,last_name,salary) values(#{firstName},#{lastName},#{salary})
</insert>
自定义主键(若数据库不支持主键自动递增,如Oracle。或 取消了主键自动递增的规则)
<insert id="addEmployee" parameterType="com.sst.cx.domain.Employee">
<!--
先使用selectKey标签定义主键,然后再定义SQL语句
1. keyProperty属性:指定对应类的id属性。
2. order属性:取值为BEFORE或AFTER。BEFORE表示先执行 <selectKey> 标签内的语句,再执行插入语句。
-->
<selectKey keyProperty="id" resultType="Integer" order="BEFORE">
select if(max(id) is null,1,max(id)+1) as newId from EMPLOYEE
</selectKey>
insert into EMPLOYEE(id,first_name,last_name,salary) values(${id},#{firstName},#{lastName},#{salary})
</insert>
insert元素的常用属性 | 描述 |
---|---|
id | 同select元素 |
parameterType | 同select元素 |
flushCache | 同select元素 |
timeout | 同select元素 |
keyProperty | 将插入操作生成的主键值赋给Java类的某个属性(通常为主键对应的属性)。如果是联合主键,可以将多个值用逗号隔开。 |
useGeneratedKeys | 是否使用JDBC提供的getGenereatedKeys()方法获取数据库内部产生的自增主键(MySQL、SQL Server)并赋值到keyProperty属性对应的对象属性中,默认值为false。和keyProperty属性组合使用可实现主键回填。 |
databaseId | 取值范围oracle、mysql等数据库厂商。元素内部可通过 <if test="_databaseId = 'oracle'"> 来为不同的数据库指定不同的sql语句。 MyBatis会加载不带databaseId属性或带有匹配当前数据库databaseId属性(优先级高)的所有语句。 |
keyColumn | 设置第几列是主键,当主键列不是表中的第1列时需要设置。如果是联合主键,可以将多个值用逗号隔开。 |
3. update元素
对应一个更新语句。
例
定义一个id为updateEmployee的更新语句
<update id="updateEmployee">
update EMPLOYEE set salary = ${salary} WHERE first_name=#{firstName} AND last_name=#{lastName}
</update>
update元素的常用属性 | 描述 |
---|---|
id | 同select元素 |
parameterType | 同select元素 |
flushCache | 同select元素 |
timeout | 同select元素 |
statementType | 同select元素。 |
4. delete元素
对应一个删除语句。
例
定义一个id为deleteEmployee的删除语句
<delete id="deleteEmployee">
delete from EMPLOYEE WHERE first_name=#{firstName} AND last_name=#{lastName}
</delete>
delete元素的常用属性 | 描述 |
---|---|
id | 同select元素 |
parameterType | 同select元素 |
flushCache | 同select元素 |
timeout | 同select元素 |
statementType | 同select元素。 |
5. resultMap元素(MyBatis中最复杂的元素)
将查询结果映射成实体对象。主要用于解决类属性名与表字段名不一致的情况。
现有的MyBatis版本只支持resultMap查询,不支持更新、保存,级联更新、删除和修改。
由以下元素构成:
<!--
id属性:resultMap的唯一标识,在select元素的resultMap属性中引用;
type属性:结果集对应的类的完全限定名
-->
<resultMap id="" type="">
<!--
当一个 POJO 没有无参数构造方法时使用,类在实例化时用来注入结果到构造方法。
-->
<constructor>
<idArg/><!-- ID参数,结果为ID -->
<arg/><!-- 注入到构造方法的一个普通结果 -->
</constructor>
<!--
注入主键到JavaBean的id属性(允许多个主键,多个主键称为联合主键)
-->
<id/>
<!--
注入普通字段到JavaBean的普通属性
id和result元素的属性:
1. property属性:类属性名
2. column属性:表字段名
3. javaType属性:Java类型(完全限定名 或 别名)
4. jdbcType属性:数据库类型
5. typeHandler属性:指定类型处理器(覆盖MyBatis默认的处理器,需要指定jdbcType和javaType相互转化的规则)。
-->
<result/>
<!-- 级联 -->
<association property=""/><!-- 用于一对一关联 -->
<collection property=""/><!-- 用于一对多、多对多关联 -->
<discriminator javaType=""><!-- 使用结果值来决定使用哪个结果映射 -->
<case value=""/><!-- 基于某些值的结果映射 -->
</discriminator>
</resultMap>
例
<resultMap type="com.sst.cx.Employee" id="myResult">
<!--
property属性:类属性名
column属性:表字段名
-->
<id property="id" column="id"/>
<result property="firstName" column="first_name"/>
</resultMap>
resultType和resultMap的区别
MyBatis的每一个查询映射的返回类型都是resultMap。resultMap和resultType不能同时使用。
当指定返回类型为resultType时,MyBatis会自动完成映射(将查询的字段映射到对应的Java对象属性中)。
当指定返回类型为resultMap时(当属性名和字段名不一致时使用,需要创建resultMap元素将字段映射到属性),MyBatis会自动完成映射。
1. MyBatis注解
1. SQL语句映射相关
1. @Insert注解:修饰插入语句。
例:
@Insert(value = "insert into EMPLOYEE(first_name,last_name,salary) values(#{firstName},#{lastName},#{salary})")
public void addEmployee(Employee employee);
2. @Select注解:修饰查询语句。
例:
@Select("Select * from user")
@Results({
@Result(id = true, column = "id", property = "id"),
@Result(column = "name", property = "name"),
@Result(column = "sex", property = "sex"),
@Result(column = "age", property = "age")
})
List<User> queryAllUser();
3. @SelectKey注解:执行插入语句后,获取id的值。
statement属性:获取id的SQL语句。
keyProperty属性:可选,将查询结果赋值给哪个类属性。
keyColumn属性:可选,将查询结果赋值给哪个表字段。
resultType属性:指定SQL语句的返回值的类型。
before属性:是否在执行插入语句之前,默认值为true。
例:
@Insert("insert into user(id,name) values(#{id},#{name})")
@SelectKey(statement = "select last_insert_id()", keyProperty = "id", keyColumn = "id", resultType = int,before = false)
public int insert(User user);
4. @Update注解:修饰更新语句
例:
@Update("update user set name= #{name},sex = #{sex},age =#{age} where id = #{id}")
void updateUserById(User user);
5. @Delete注解:修饰删除语句
例:
@Delete("delete from user where id =#{id}")
void deleteById(Integer id);
6. @Param注解:修饰方法参数
value属性:指定别名
例:
int saveUser(@Param(value="user") User user,@Param("name") String name,@Param("age") Int age);
2. 结果集映射相关
@Result、@Results、@ResultMap
id属性:当前结果集的唯一标识。
value属性:可选。映射字段到Java类属性。
@Result注解:代表一个字段的映射关系(property指定属性名;column指定字段名;jdbcType:字段类型;id为true表示为主键)。
例:
@Select({"select id, name, class_id from student"})
@Results(id="studentMap", value={
@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
@Result(column="name", property="name", jdbcType=JdbcType.VARCHAR),
@Result(column="class_id ", property="classId", jdbcType=JdbcType.INTEGER)
})
List<Student> selectAll();
3. 关系映射相关
1. @one注解:用于一对一关系映射
例:
@Select("select * from student")
@Results({
@Result(id=true,property="id",column="id"),
@Result(property="name",column="name"),
@Result(property="age",column="age"),
@Result(property="address",column="address_id",one=@One(select="com.sst.cx.mapper.AddressMapper.getAddress"))
})
public List<Student> getAllStudents();
2. @many:用于一对多关系映射
例:
@Select("select * from t_class where id=#{id}")
@Results({
@Result(id=true,column="id",property="id"),
@Result(column="class_name",property="className"),
@Result(property="students", column="id", many=@Many(select="com.sst.cx.mapper.StudentMapper.getStudentsByClassId"))
})
public Class getClass(int id);
2. 级联查询
获取关联数据时十分便捷,但级联过多(超过3层时不再使用)会增加系统的复杂度和性能。
3种关联关系:
1. 一对一(如:学生和学号)
通过resultMap元素的association子元素处理一对一级联关系。
1. property属性:对象属性的名称。
2. javaType属性:对象属性的类型(完全限定名)。
3. column属性:表字段名(对应的外键)。
4. select属性:指定嵌套查询的子SQL。
例:
<association property="studentCard" column="card_id" javaType="com.sst.cx.domain.StudentCard" select="com.sst.cx.mapper.StudentCardMapper.selectStudentCardById" />
2. 一对多(如:班级和学生)
通过resultMap元素的collection子元素处理一对多级联关系(会将关联查询的多条记录映射到一个list集合属性中)。
例:
<collection property="studentList" ofType="com.sst.cx.domain.Student" column="id" select="com.sst.cx.mapper.StudentMapper.selectStudentById" />
3. 多对多(如:学生和课程)
MyBatis没有实现多对多级联,推荐通过两个一对多级联替换多对多级联,以降低关系的复杂度,简化程序。
示例(一对一)
===》实体类
public class Student {
private int id;
private String name;
private int sex;
private StudentCard studentCard;
/* 省略setter和getter方法 */
}
public class StudentCard {
private int id;
private int studentId;
/* 省略setter和getter方法 */
}
===》xml映射文件
方式1. 单步查询(通过关联查询实现)
<resultMap type="com.sst.cx.domian.Student" id="cardAndStudent">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="sex" column="sex" />
<!-- 一对一级联查询 -->
<association property="studentCard" javaType="com.sst.cx.domian.StudentCard">
<id property="id" column="id" />
<result property="studentId" column="student_id" />
</association>
</resultMap>
<select id="selectStudentById" parameterType="Integer" resultMap="cardAndStudent">
SELECT * FROM student s,studentCard sc WHERE s.card_id = sc.id AND s.id=#{id}
</select>
方式2. 分步查询(通过多次查询实现)
1. StudentCardMapper.xml:
<mapper namespace="com.sst.cx.mapper.StudentCardMapper">
<select id="selectStudentCardById" resultType="com.sst.cx.domian.StudentCard">
SELECT * FROM studentCard WHERE id = #{id}
</select>
</mapper>
2. StudentMapper.xml:
<mapper namespace="com.sst.cx.mapper.StudentMapper">
<!-- 嵌套查询(会执行两个SQL语句) -->
<resultMap type="com.sst.cx.domian.Student" id="cardAndStudent">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="sex" column="sex" />
<!-- 一对一级联查询 -->
<association property="studentCard" column="card_id" javaType="com.sst.cx.domian.StudentCard" select="com.sst.cx.mapper.StudentCardMapper.selectStudentCardById" />
</resultMap>
<select id="selectStudentById" parameterType="Integer" resultMap="cardAndStudent">
select * from student where id=#{id}
</select>
</mapper>
===》映射接口(StudentMapper.java)
public Student selectStudentById(int id);
示例(一对多)
===》实体类
public class User {
private int id;
private String name;
private String pwd;
private List<Order> orderList;
/*省略setter和getter方法*/
}
public class Order {
private int userId;
private int orderNum;
/*省略setter和getter方法*/
}
===》xml映射文件
方式1. 单步查询(通过关联查询实现)
<resultMap type="com.sst.cx.domian.User" id="userAndOrder">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="pwd" column="pwd" />
<!-- ofType属性:表示集合中的元素类型 -->
<collection property="orderList" ofType="com.sst.cx.domian.Order">
<id property="orderId" column="order_id" />
<result property="orderNum" column="order_num" />
</collection>
</resultMap>
<select id="selectUserOrderById" parameterType="Integer" resultMap="userAndOrder">
SELECT * FROM user u,order o WHERE u.id=o.user_id AND u.id=#{id}
</select>
方式2. 分步查询(通过多次查询实现)
1. OrderMapper.xml:
<!-- 根据id查询订单信息 -->
<mapper namespace="com.sst.cx.mapper.OrderMapper">
<select id="selectOrderById" resultType="com.sst.cx.domian.Order" parameterType="Integer">
SELECT * FROM order where user_id=#{userId}
</select>
</mapper>
2. UserMapper.xml:
<mapper namespace="com.sst.cx.mapper.UserMapper">
<resultMap type="com.sst.cx.domian.User" id="userAndOrder">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="pwd" column="pwd" />
<!-- ofType属性:表示集合中的元素类型,column属性:将id传递给selectOrderById -->
<collection property="orderList" ofType="com.sst.cx.domian.Order" column="id" select="com.sst.cx.mapper.OrderMapper.selectOrderById" />
</resultMap>
<select id="selectUserOrderById" parameterType="Integer" resultMap="userAndOrder">
select * from user where id=#{id}
</select>
</mapper>
示例(多对多)
===》实体类
public class Order {
private int oid;
private int ordernum;
private List<Product> products;
/*省略setter和getter方法*/
}
public class Product {
private int pid;
private String name;
private Double price;
private List<Order> orders;
/*省略setter和getter方法*/
}
===》xml映射文件
OrderMapper.xml:
<mapper namespace="com.sst.cx.mapper.OrderMapper">
<resultMap type="com.sst.cx.domian.Order" id="orderMap">
<id property="oid" column="oid" />
<result property="ordernum" column="ordernum" />
<collection property="products" ofType="com.sst.cx.domian.Product">
<id property="pid" column="pid" />
<result property="name" column="name" />
<result property="price" column="price" />
</collection>
</resultMap>
<select id="selectAllOrdersAndProducts" parameterType="Integer" resultMap="orderMap">
SELECT o.oid,o.ordernum,p.pid,p.name,p.price FROM
order o
INNER JOIN orders_detail od ON o.oid=od.orderId
INNER JOIN
product p
ON p.pid = od.productId
</select>
</mapper>
3. 动态SQL(减少代码工作量)
1. if元素(没有else功能)
判断语句(常用于:当条件为true时拼接where语句)
<if test="判断条件">
SQL语句
</if>
例:
<select id="selectAllWebsite" resultMap="myResult">
select id,name,url from website where 1=1
<if test="name != null">
AND name like #{name}
</if>
<if test="url!= null">
AND url like #{url}
</if>
</select>
2. choose(when、otherwise)元素
相当于switch-case语句
<choose>
<when test="判断条件1">
SQL语句1
</when >
<when test="判断条件2">
SQL语句2
</when >
<when test="判断条件3">
SQL语句3
</when >
<otherwise>
SQL语句4
</otherwise>
</choose>
例:
<mapper namespace="com.sst.cx.mapper.WebsiteMapper">
<select id="selectWebsite"
parameterType="com.sst.cx.domian.Website"
resultType="com.sst.cx.domian.Website">
SELECT id,name,url,age,country
FROM website WHERE 1=1
<choose>
<when test="name != null and name !=''">
AND name LIKE CONCAT('%',#{name},'%')
</when>
<when test="url != null and url !=''">
AND url LIKE CONCAT('%',#{url},'%')
</when>
<otherwise>
AND age is not null
</otherwise>
</choose>
</select>
</mapper>
3. where元素
用于生成SQL的where字符串,否则需要加WHERE 1=1。
<where>
<if test="判断条件">
AND/OR ...
</if>
</where>
例(不需要再加WHERE 1=1)
<select id="selectWebsite" resultType="com.sst.cx.domian.Website">
select id,name,url from website
<where>
<if test="name != null">
AND name like #{name}
</if>
<if test="url != null">
AND url like #{url}
</if>
</where>
</select>
4. trim元素
用于选择性插入、更新、删除或者条件查询等操作(去除SQL语句中多余的AND关键字、逗号;给SQL语句拼接where、set等后缀)。
<trim prefix="前缀" suffix="后缀" prefixOverrides="替换前缀字符" suffixOverrides="替换后缀字符">
SQL语句
</trim>
1. prefix属性:给SQL语句拼接的前缀(为trim元素包含的内容加上前缀)。
2. suffix属性:给SQL语句拼接的后缀(为trim元素包含的内容加上后缀)。
3. prefixOverrides属性:替代SQL语句前面的关键字或字符。
4. suffixOverrides属性:替代SQL语句后面的关键字或者字符。
例
<select id="selectWebsite" resultType="net.biancheng.po.Website">
SELECT id,name,url,age,country
FROM website
<trim prefix="where" prefixOverrides="and">
<if test="name != null and name !=''">
AND name LIKE CONCAT ('%',#{name},'%')
</if>
<if test="url!= null">
AND url like concat ('%',#{url},'%')
</if>
</trim>
</select>
4. foreach元素
循环语句(用于配合in关键字)。
应提前预估集合的长度(过长影响性能;部分数据库有SQL长度限制)。
<foreach item="item" index="index" collection="list|array|map key" open="(" separator="," close=")">
参数值
</foreach>
1. item属性:表示集合中每一个元素进行迭代时的别名。
2. index属性:表示在迭代过程中每次迭代到的位置。
3. collection 属性:必填。
如果传入的是单参数且参数类型是一个 List,collection 属性值为 list。
如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array。
如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。
4. open属性:表示该语句以什么开始(既然是 in 条件语句,所以必然以(开始)。
5. separator属性:表示在每次进行迭代之间以什么符号作为分隔符(既然是 in 条件语句,所以必然以,作为分隔符)。
6. close属性:表示该语句以什么结束(既然是 in 条件语句,所以必然以)开始)。
例
<select id="selectWebsite" parameterType="com.sst.cx.domian.Website" resultType="com.sst.cx.domian.Website">
SELECT id,name,url,age,country
FROM website WHERE age in
<foreach item="age" index="index" collection="list" open="("
separator="," close=")">
#{age}
</foreach>
</select>
5. bind元素
用于对参数进行特殊处理后,再拼接到SQL中。
每个数据库的拼接函数或连接符号不同(如: MySQL的concat 函数、Oracle的连接符号“||”等),这样SQL映射文件就需要根据不同的数据库提供不同的实现,不利于代码的移植而且麻烦。因此,MyBatis 提供了bind标签来解决这一问题。
1. name属性:给对应参数取的别名。
2. value属性:给对应参数进行特殊处理。
例
<select id="selectWebsite" resultType="com.sst.cx.domian.Website">
<bind name="pattern_name" value="'%'+name+'%'" />
<bind name="pattern_url" value="'%'+url+'%'" />
SELECT id,name,url,age,country FROM website
WHERE name like #{pattern_name}
AND url like #{pattern_url}
</select>
6. set元素
添加set关键字(用于更新语句),去除条件尾部多余的逗号。
例
<mapper namespace="com.sst.cx.mapper.WebsiteMapper">
<select id="selectWebsite" resultType="om.sst.cx.domian.Website">
SELECT * FROM website
<where>
<if test="id!=null and id!=''">
id=#{id}
</if>
</where>
</select>
<!--使用set元素动态修改一个网站记录 -->
<update id="updateWebsite" parameterType="com.sst.cx.domian.Website">
UPDATE website
<set>
<if test="name!=null">name=#{name}</if>
<if test="url!=null">url=#{url}</if>
</set>
WHERE id=#{id}
</update>
</mapper>
4. 分页(属于DAO层操作)
MyBatis的分页功能是基于内存的分页(即先查询出所有记录,再按起始位置和页面容量取出结果)。
例
===》WebsiteMapper.java
public List<Website> selectWebsite(@Param("site") Website site, @Param("from") Integer currentPageNo,@Param("pageSize") Integer pageSize);
===》WebsiteMapper.xml
<select id="selectWebsite" resultType="com.sst.cx.domian.Website">
SELECT id,name,url,age,country FROM website
<trim prefix="where" prefixOverrides="and">
<if test="site.name != null and site.name !=''">
AND name LIKE CONCAT ('%',#{site.name},'%')
</if>
<if test="site.url!= null and site.url !=''">
AND url LIKE CONCAT ('%',#{site.url},'%')
</if>
ORDER BY id limit #{from},#{pageSize}
</trim>
</select>
===》测试
Website site = new Website();
site.setUrl("http");
Integer pageSize = 3;
Integer currentPageNo = 0;
List<Website> siteList = new ArrayList<Website>();
siteList = sqlSession.getMapper(WebsiteMapper.class).selectWebsite(site, currentPageNo, pageSize);
for (Website ws : siteList) {
System.out.println(ws);
}
5. 缓存
MyBatis支持一级缓存(默认开启)和二级缓存。
1. 都是基于PerpetualCache(MyBatis自带)的HashMap缓存,但作用域不同。
2. 执行R查询操作会将查询结果进行缓存。
3. 执行CUD增删改操作后,缓存会被清空。手动清空缓存:session.clearCache(); 。如果指定了刷新时间间隔flushInterval,会定时清空缓存。
4. 使用(一级缓存:必须是同一个;二级缓存:可以不同)SqlSession对象执行同一个查询SQL(且参数相同)时,(如果缓存没有清空或超时)会从缓存中直接获取结果。
一级缓存(Session缓存,默认开启)
生命周期和Session保持一致(当Session调用flush或close后,缓存中所有的数据会被清空)。一级缓存是Session独享的(每个Session不能访问其他Session的缓存区)
二级缓存(全局缓存/跨session缓存:可以被所有SqlSession共享)
可以自定义存储源。
使用步骤:
1. 在核心配置文件中开启二级缓存。
<settings>
<setting name="cacheEnabled" value="true" />
</settings>
2. 在映射文件中配置缓存。
<mapper namescape="com.sst.cx.mapper.WebsiteMapper">
<!--
cache配置
可以直接<cache/>全部使用默认配置,也可以按需求修改cache参数。
-->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true" />
<select id="getWebsiteList" resultType="com.sst.cx.domain.Website" usecache="true">
...
</select>
...
</mapper>
说明:
1. eviction属性(缓存回收策略)
1. LRU:默认。使用较少,移除最长时间不用的对象;
2. FIFO:先进先出,按对象进入缓存的顺序来移除它们;
3. SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象;
4. WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象。
2. flushInterval属性(刷新间隔时间,单位为毫秒)
默认执行SQL时才会刷新缓存。
3. size属性(缓存最多可以存储的个数,正整数)
默认1024,不宜设置过大(过大会导致内存溢出)。
4. readOnly属性(缓存数据是否只能读取而不能修改)
默认值为false,可以快速读取缓存但不能修改缓存。
6. 存储过程
示例
===》创建表和存储过程
create table p_user(
id int primary key auto_increment,
name varchar(10),
sex char(2)
);
insert into p_user(name,sex) values('A',"男");
insert into p_user(name,sex) values('B',"女");
insert into p_user(name,sex) values('C',"男");
-- 创建存储过程(查询得到男性或女性的数量, 传入0表示女性否则是男性)
DELIMITER $
CREATE PROCEDURE mybatis.ges_user_count(IN sex_id INT, OUT user_count INT)
BEGIN
IF sex_id=0 THEN
SELECT COUNT(*) FROM mybatis.p_user WHERE p_user.sex='女' INTO user_count;
ELSE
SELECT COUNT(*) FROM mybatis.p_user WHERE p_user.sex='男' INTO user_count;
END IF;
END
$
-- 调用存储过程
DELIMITER ;
SET @user_count = 0;
CALL mybatis.ges_user_count(1, @user_count);
SELECT @user_count;
===》userMapper.xml
<!--
查询得到男性或女性的数量, 如果传入的是0就女性否则是男性
-->
<select id="getUserCount" parameterMap="getUserCountMap" statementType="CALLABLE">
CALL mybatis.ges_user_count(?,?)
</select>
<!--
parameterMap.put("sexid", 0);
parameterMap.put("usercount", -1);
-->
<parameterMap type="java.util.Map" id="getUserCountMap">
<parameter property="sexid" mode="IN" jdbcType="INTEGER"/>
<parameter property="usercount" mode="OUT" jdbcType="INTEGER"/>
</parameterMap>
===》测试
Map<String, Integer> parameterMap = new HashMap<String, Integer>();
parameterMap.put("sexid", 1);
parameterMap.put("usercount", -1);
sqlSession.selectOne(com.sst.cx.mapping.userMapper.getUserCount, parameterMap);
Integer result = parameterMap.get("usercount");
7. (根据数据库表)自动生成:POJO类、mapper映射文件、mapper 接口
Mybatis提供了一个逆向工程工具,可以根据数据表自动生成针对单表的POJO类、mapper映射文件、mapper 接口。
从Github下载mybatis-generator-core-xxx.jar
示例
===》1. 添加依赖
如果使用maven项目时,只需在pom.xml中添加依赖(不再需要手动导入jar包)
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.0</version>
</dependency>
如果不是maven项目,需要导入依赖包(复制mybatis-generator-core-xxx.jar到WEB-INF/lib目录下)。
===》2. 创建表(user、student、studentCard、website)
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
`sex` tinyint(4) DEFAULT NULL,
`cardId` int(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `cardId` (`cardId`),
CONSTRAINT `student_ibfk_1` FOREIGN KEY (`cardId`) REFERENCES `studentcard` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `studentcard`;
CREATE TABLE `studentcard` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`studentId` int(20) DEFAULT NULL,
`startDate` date DEFAULT NULL,
`endDate` date DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `studentId` (`studentId`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`pwd` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `website`;
CREATE TABLE `website` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
`url` varchar(30) COLLATE utf8_unicode_ci DEFAULT '',
`age` tinyint(3) unsigned NOT NULL,
`country` char(3) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`createtime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
===》3. generatorConfig.xml配置文件(在项目根目录/config目录下创建)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- Mysql数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/test" userId="root"
password="12345678" />
<!-- 默认为false,把JDBC DECIMAL 和NUMERIC类型解析为Integer,为true时 把JDBC DECIMAL 和NUMERIC类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成POJO类的位置 (maven项目路径为./src/main/java)-->
<javaModelGenerator
targetPackage="com.sst.cx.domain" targetProject="./src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.sst.cx.mapper"
targetProject="./src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetProject:mapper接口生成的的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.sst.cx.dao" targetProject="./src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定数据表 -->
<table tableName="website"></table>
<table tableName="student"></table>
<table tableName="studentcard"></table>
<table tableName="user"></table>
</context>
</generatorConfiguration>
===》4. GeneratorSqlmap.java(运行后会自动生成POJO类、Mapper类、Mapper映射文件)
package com.sst.cx;
import java.io.File;
import java.util.*;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
public class GeneratorSqlmap {
public void generator() throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
// 指定配置文件
File configFile = new File("./config/generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
// 执行main方法以生成代码
public static void main(String[] args) {
try {
GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行后,可以看到在pojo包中,有一部分是名字为 XxxExample 的类,类中包含以下3个成员变量,如下:
1. protected String orderByClause;
用于指定ORDER BY条件,这个条件没有构造方法,直接通过传递字符串值指定。
2. protected boolean distinct;
用于指定DISTINCT查询。
3. protected List<Criteria> oredCriteria;
用于自定义查询条件。
也可以在终端执行如下命令来自动生成
java -jar mybatis-generator-core-1.3.2.jar -configfile generatorConfig.xml -overwrite
8. Maven+Spring+MyBatis
1. 创建MavenWeb项目
方式1. 在终端执行如下命令
还需要手动创建如下目录:src/main/java、src/test/java、src/test/resources。
导入到Eclipse中。
mvn archetype:generate -DgroupId=com.sstx -DartifactId=SpringMavenMyBatis -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
方式2. 使用Eclipse、MyEclipse等开发工具创建
2. 编辑pom.xml文件内容
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sst.cx</groupId>
<artifactId>SpringMavenMyBatis</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>SpringMavenMyBatis</name>
<url>http://maven.apache.org</url>
<!-- 解决报错(不再支持源选项 5。请使用 7 或更高版本) -->
<properties>
<project.build.sourceencoding>UTF-8</project.build.sourceencoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- 用于:自动生成POJO类、Mapper映射文件、Mapper接口 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.1</version>
</dependency>
<!-- 添加mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<!-- 添加mysql依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<!-- 添加junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<!-- 添加servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!-- 添加jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 添加druid连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.14</version>
</dependency>
<!-- 添加spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.23</version>
</dependency>
<!-- 添加Aspectj依赖 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9.1</version>
</dependency>
</dependencies>
<build>
<finalName>SpringMavenMyBatis</finalName>
<plugins>
<!-- 用于:解决pom.xml文件报错 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
</plugins>
<!-- 解决com.sst.cx.mapper包下xml文件没有打包到target目录中 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>
src/main/resources
</directory>
</resource>
</resources>
</build>
</project>
3. 创建数据库和表
Create DATABASE Test;
USE Test;
DROP TABLE IF EXISTS EMPLOYEE;
create table EMPLOYEE (
id INT NOT NULL auto_increment,
first_name VARCHAR(20) default NULL,
last_name VARCHAR(20) default NULL,
salary INT default NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
4. 根据数据库表自动生成POJO类、Mapper映射文件、Mapper接口
在项目根目录/config目录下创建genertorConfig.xml配置文件(内容同上,修改数据库表名)。
在com.sst.cx包下创建GeneratorSqlmap.java(内容同上),并运行(运行后删除)。
自动生成的结果
5. 编写相关配置文件
在src/main/resources目录下创建
===》1. database.properties文件(数据库连接参数)
jdbc_driver = com.mysql.cj.jdbc.Driver
jdbc_url = jdbc:mysql://localhost:3306/Test?characterEncoding=utf8
jdbc_username = root
jdbc_password = 12345678
validationQuery = SELECT 1
===》2. spring.xml文件(springIoc容器配置---spring)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--
引入database.properties文件
-->
<context:property-placeholder location="classpath:database.properties"/>
<!--
自动扫描包下的类,从类的注解中获取Bean的定义信息。
-->
<context:component-scan base-package="com.sst.cx.service"/>
</beans>
===》3. spring-mybatis.xml文件(springIoc容器配置---spring+mybatis)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!--
配置数据源
-->
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc_url}" />
<property name="username" value="${jdbc_username}" />
<property name="password" value="${jdbc_password}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="0" />
<!-- 连接池最大使用连接数量 -->
<property name="maxActive" value="20" />
<!-- 连接池最大空闲 -->
<property name="maxIdle" value="20" />
<!-- 连接池最小空闲 -->
<property name="minIdle" value="0" />
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="60000" />
<!--
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="33" />
-->
<property name="validationQuery" value="${validationQuery}" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="testWhileIdle" value="true" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="25200000" />
<!-- 打开removeAbandoned功能 -->
<property name="removeAbandoned" value="true" />
<!-- 1800秒,也就是30分钟 -->
<property name="removeAbandonedTimeout" value="1800" />
<!-- 关闭abanded连接时输出错误日志 -->
<property name="logAbandoned" value="true" />
<!-- 监控数据库 mergeStat、stat、-->
<property name="filters" value="mergeStat" />
</bean>
<!--
配置Mybatis
-->
<!-- 配置sqlSessionFactory(数据源、mapper映射文件) -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:com/sst/cx/mapper/*.xml" />
</bean>
<!-- 配置扫描器(扫描包下的所有mapper接口) -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.sst.cx.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!--
配置事务
-->
<!-- 配置Spring的事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 注解方式配置事务:<tx:annotation-driven transaction-manager="transactionManager"/> -->
<!-- 配置事务通知(指定事务作用于哪些类的哪些方法,对方法调用前后进行拦截,添加事务逻辑代码) -->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="append*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="modify*" propagation="REQUIRED" />
<tx:method name="edit*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="repair" propagation="REQUIRED" />
<tx:method name="delAndRepair" propagation="REQUIRED" />
<tx:method name="get*" propagation="SUPPORTS" />
<tx:method name="find*" propagation="SUPPORTS" />
<tx:method name="load*" propagation="SUPPORTS" />
<tx:method name="search*" propagation="SUPPORTS" />
<tx:method name="datagrid*" propagation="SUPPORTS" />
<tx:method name="*" propagation="SUPPORTS" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="transactionPointcut" expression="execution(* com.sst.cx.service.*Impl.*(..))" />
<aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
</aop:config>
<!-- 配置druid监控springJdbc -->
<bean id="druid-stat-interceptor" class="com.alibaba.druid.support.spring.stat.DruidStatInterceptor">
</bean>
<bean id="druid-stat-pointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut" scope="prototype">
<property name="patterns">
<list>
<value>com.sst.cx.service.*</value>
</list>
</property>
</bean>
<aop:config>
<aop:advisor advice-ref="druid-stat-interceptor" pointcut-ref="druid-stat-pointcut" />
</aop:config>
</beans>
6. 添加业务逻辑
===》EmployeeService.java(com.sst.cx.service)
package com.sst.cx.service;
import com.sst.cx.domain.Employee;
public interface EmployeeService {
// 添加员工
void addEmployee(Employee employee);
// 根据id获取员工
Employee getEmployeeById(int userId);
}
===》EmployeeServiceImpl.java(com.sst.cx.service.impl)
package com.sst.cx.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.sst.cx.dao.EmployeeMapper;
import com.sst.cx.domain.Employee;
import com.sst.cx.service.EmployeeService;
@Service("employeeServiceImpl")
public class EmployeeServiceImpl implements EmployeeService{
@Autowired
private EmployeeMapper employeeMapper;
public void addEmployee(Employee employee) {
employeeMapper.insert(employee);
}
public Employee getEmployeeById(int userId) {
return employeeMapper.selectByPrimaryKey(userId);
}
}
7. 测试
===》MyBatisTest.java(src/test/java目录下创建com.sst.cx.test包)常规Junit测试框架
package com.sst.cx.test;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.sst.cx.domain.Employee;
import com.sst.cx.service.EmployeeService;
public class MyBatisTest {
private EmployeeService employeeService;
private ApplicationContext context;
/**
* before方法:在所有的测试方法之前执行,且只执行一次。
* 在进行Junit单元测试时,负责做一些初始化工作(如:初始化ApplicationContext)
*/
@Before
public void before() {
// 获取SpringIoc容器
context = new ClassPathXmlApplicationContext(new String[] { "spring.xml", "spring-mybatis.xml" });
// 从SpringIoc容器中获取EmployeeServiceImpl对象
employeeService = (EmployeeService) context.getBean("employeeServiceImpl");
}
@Test
public void testAddEmployee() {
Employee employee = new Employee();
employee.setFirstName("张");
employee.setLastName("三");
employee.setSalary(500000);
employeeService.addEmployee(employee);
}
@Test
public void testGetEmployeeById() {
Employee employee = employeeService.getEmployeeById(39);
System.out.println(employee.getFirstName() + employee.getLastName());
}
}
===》MyBatisSpringTest.java(src/test/java目录的com.sst.cx.test包下创建)Spring的Junit测试框架
package com.sst.cx.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.sst.cx.domain.Employee;
import com.sst.cx.service.EmployeeService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring.xml", "classpath:spring-mybatis.xml" })
public class MyBatisSpringTest {
@Autowired
private EmployeeService employeeService;
@Test
public void testAddEmployee(){
Employee employee = new Employee();
employee.setFirstName("张");
employee.setLastName("三");
employee.setSalary(500000);
employeeService.addEmployee(employee);
}
@Test
public void testGetEmployeeById(){
Employee employee = employeeService.getEmployeeById(39);
System.out.println(employee.getFirstName()+employee.getLastName());
}
}
进行JUnit测试,会在数据库中插入一条数据
8. 测试2(获取所有员工信息并展示在页面中)
===》1. 编辑web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<listener>
<description>Spring监听器</description>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- ContextLoaderListener初始化Spring上下文时需要使用到的contextConfigLocation参数 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml,classpath:spring-mybatis.xml</param-value>
</context-param>
</web-app>
===》2. 在EmployeeMapper.xml映射文件中添加
<!-- 按需求进行扩展 -->
<select id="getAllEmployee" resultMap="BaseResultMap">
select * from EMPLOYEE
</select>
===》3. 在EmployeeMapper.java接口中添加
// 按需求进行扩展
// 获取所有员工信息
List<Employee> getAllEmployee();
===》4. 在EmployeeService.java中添加
// 获取所有员工信息
List<Employee> getAllEmployee();
===》5. 在EmployeeServiceImpl.java中添加
// 获取所有员工信息
public List<Employee> getAllEmployee() {
return employeeMapper.getAllEmployee();
}
===》6. 创建EmployeeServlet.java(在src/main/java目录的com.sst.cx.web.controller包下)
package com.sst.cx.web.controller;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.sst.cx.domain.Employee;
import com.sst.cx.service.EmployeeService;
@WebServlet("/EmployeeServlet")
public class EmployeeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private EmployeeService employeeService;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取所有的员工信息,添加到request域中
List<Employee> employeeList = employeeService.getAllEmployee();
request.setAttribute("employeeList", employeeList);
request.getRequestDispatcher("/index.jsp").forward(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
public void init() throws ServletException {
// 获取Ioc容器
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
// 从Ioc容器中获取employeeServiceImpl
employeeService = (EmployeeService) context.getBean("employeeServiceImpl");
}
}
===》7. 编辑index.jsp文件
<%@ page language="java" pageEncoding="UTF-8"%>
<%--引入JSTL核心标签库 --%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<title>显示所有员工信息</title>
<style type="text/css">
table,td{
border: 1px solid;
border-collapse: collapse;
}
</style>
</head>
<body>
<table>
<tr>
<td>员工的ID</td>
<td>员工的名字</td>
<td>员工的工资</td>
</tr>
<%--遍历lstUsers集合中的User对象 --%>
<c:forEach var="employee" items="${employeeList}">
<tr>
<td>${employee.id}</td>
<td>${employee.firstName}${employee.lastName}</td>
<td>${employee.salary}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
===》8. 打包并放置在Tomcat/webapps目录下
在浏览器中输入http://127.0.0.1:8080/SpringMavenMyBatis/EmployeeServlet
运行结果
网友评论