MyBatis
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
1、核心配置文件
resources/mybatis-config.xml全局配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--环境配置-->
<environments default="yan">
<!--某个环境配置-->
<environment id="yan">
<!--事务管理器,jdbc/manager -->
<transactionManager type="JDBC"/>
<!--数据源配置,unpooled/pooled/jndi-->
<dataSource type="POOLED">
<!-- 创建连接的基本配置 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///test?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<!-- 还有连接池的其它配置参数-->
</dataSource>
</environment>
</environments>
<!-- 注册映射元文件-->
<mappers>
<mapper resource="com/yan/dao/UserMapper.xml"/>
</mappers>
</configuration>
2、创建表
- 分析需求
- 定义模式
- 规范化
drop database test;
create database test default character set utf8;
use test;
create table t_users(
id bigint primary key auto_increment,
username varchar(20) not null unique,
password varchar(20) not null,
birth datetime default now(),
sex boolean default 1,
salary numeric(8,2)
)engine=innodb default charset utf8;
3、根据表结构定义对应的实体类
@Data
public class UserBean implements Serializable {
private Long id;
private String username;
private String password;
private Date birth;
private Boolean sex;
private Double salary;
}
4、定义映射元文件
一般名称方式为【表名/实体类名-mapper.xml】
resources/com/yan/mapper/UserMapper.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.yan.dao.UserMapper">
</mapper>
5、为了保留测试代码,所以引入 junit—回归测试\单元测试
添加依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
测试类
test/java目录下
public class UserMapperTest {
private SqlSessionFactory factory;
@Before //声明bb方法是在每个test方法执行之前执行
public void bb()throws Exception{
Reader r= Resources.getResourceAsReader("mybatis-config.xml");
factory=new SqlSessionFactoryBuilder().build(r);
}
@Test //测试方法
public void test1(){}
}
插入数据
MyBatis不会生成sql语句,所以任何操作都需要在映射元文件种定义相关的sql语句
<?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.yan.dao.UserMapper">
<insert id="save" parameterType="com.yan.entity.UserBean">
insert into t_users(username,password) values(#{username},#{password})
</insert>
</mapper>
然后在接口中定义对应的方法声明
public interface UserMapper {
int save(UserBean user);
}
单元测试
public class UserMapperTest {
private SqlSessionFactory factory;
@Before
public void bb()throws Exception{
//读取mybatis-config.xml的字符流
Reader r= Resources.getResourceAsReader("mybatis-config.xml");
//创建SQLSessionFactory对象
factory=new SqlSessionFactoryBuilder().build(r);
}
@Test
public void testSave(){
//创建SqlSession对象。SqlSession充当实体管理器功能,提供最基本的CRUD操作方法
SqlSession session=factory.openSession();
//获取Mapper接口的代理对象
UserMapper userMapper=session.getMapper(UserMapper.class);
UserBean user=new UserBean();
user.setUsername("猴子");
user.setPassword("123456");
//调用save方法
int len=userMapper.save(user);
session.commit(); //提交事务
session.close(); //关闭session会话
}
}
插入完成后要求返回自增长的id值
针对mysql的auto_increment,在配置上添加设置useGeneratedKeys=“true”
<insert id="save" parameterType="com.yan.entity.UserBean" useGeneratedKeys="true" keyProperty="id">
insert into t_users(username,password) values(#{username},#{password})
</insert>
修改单元测试
public class UserMapperTest {
private SqlSessionFactory factory;
@Before
public void bb()throws Exception{
Reader r= Resources.getResourceAsReader("mybatis-config.xml");
factory=new SqlSessionFactoryBuilder().build(r);
}
@Test
public void testSave(){
SqlSession session=factory.openSession();
UserMapper userMapper=session.getMapper(UserMapper.class);
UserBean user=new UserBean();
user.setUsername("猴子");
user.setPassword("123456");
int len=userMapper.save(user);
session.commit();
session.close();
Assert.assertEquals(1,len);
if(len>0){
System.out.println(user.getId());
}
}
}
删除数据
- 逻辑删除和物理删除
修改映射元文件,添加delete操作配置
<delete id="remove" parameterType="long">
delete from t_users where id=#{id} //针对简单类型,#{}用于指代这个参数,而不是参数中的某个属性值,这个名称可以随意写
</delete>
修改接口,新增方法
public interface UserMapper {
int save(UserBean user);
int remove(Long id); //对应映射元文件种的<delete>
}
单元测试
@Test
public void testRemove(){
SqlSession session=factory.openSession();
UserMapper userMapper=session.getMapper(UserMapper.class);
int len=userMapper.remove(1L);
if(len>0)
System.out.println("删除成功!");
Assert.assertEquals(1,len);
session.commit();
session.close();
}
按照id加载数据
修改映射元文件,添加select操作配置
<select id="load" parameterType="long" resultType="com.yan.entity.UserBean">参数是long类型,而返回值为UserBean类型
select * from t_users where id=#{id}
</select>
修改接口,新增方法
public interface UserMapper {
int save(UserBean user);
int remove(Long id);
UserBean load(Long id); //对应的就是<select id="load">
}
单元测试
@Test
public void testLoad(){
SqlSession session=factory.openSession();
UserMapper userMapper=session.getMapper(UserMapper.class);
UserBean user=userMapper.load(3L);
System.out.println(user);
session.commit();
session.close();
}
修改操作
按照id修改数据
修改映射元文件,添加update操作配置
<update id="modify" parameterType="com.yan.entity.UserBean">
update t_users set username=#{username},password=#{password} where id=#{id}
</update>
修改接口,新增方法
public interface UserMapper {
int save(UserBean user);
int remove(Long id);
UserBean load(Long id);
int modify(UserBean user); //对应<update id="modify">对应名称
}
单元测试
@Test
public void testModify() {
SqlSession session = factory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
UserBean user = userMapper.load(3L);
user.setUsername("小吕");
user.setPassword("666666");
int len=userMapper.modify(user);
if(len>0) System.out.println("修改成功!");
session.commit();
session.close();
}
查询所有数据
修改映射元文件,添加select操作配置
<select id="selectAll" resultType="com.yan.entity.UserBean"> 注意虽然返回的是一个集合,但是不能写集合类型,应该定义集合元素的类型
select * from t_users
</select>
修改接口,新增方法
public interface UserMapper {
int save(UserBean user);
int remove(Long id);
UserBean load(Long id);
int modify(UserBean user);
List<UserBean> selectAll(); //对应的是<select id="selectAll">
}
单元测试
@Test
public void testSelectAll() {
SqlSession session = factory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
List<UserBean> userList=userMapper.selectAll();
userList.forEach(System.out::println);
session.close();
}
查询总行数
修改映射元文件,添加select操作配置
<select id="selectCount" resultType="long"> 返回结果值为long类型,没有参数
select count(*) from t_users
</select>
修改接口,新增方法
public interface UserMapper {
... ...
long selectCount();//对应<select id="selectCount">
}j
单元测试
@Test
public void testSelectCount() {
SqlSession session = factory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
long len=userMapper.selectCount();
System.out.println(len);
Assert.assertEquals(2,len);
session.close();
}
不使用接口的调用方式
- 属于比较老的调用方法
int len=session.insert("com.yan.dao.UserMapper.save",user); insert表示执行<insert>操作,对应的id为[namespace+id]
int len=session.delete("com.yan.dao.UserMapper.remove",1L);
UserBean user=session.selectOne("com.yan.dao.UserMapper.load",3L);
int len=session.update("com.yan.dao.UserMapper.modify",user);
List<UserBean> userList=session.selectList("com.yan.dao.UserMapper.selectAll");
long len=session.selectOne("com.yan.dao.UserMapper.selectCount");
最大的问题是:不能进行静态检查
全局核心配置
- 文件名称建议使用mybatis-config.xml,但是不是必须,可以改名称,甚至修改位置
Reader r= Resources.getResourceAsReader("mybatis-config.xml"); //对应位置为resources根目录下
Reader r= Resources.getResourceAsReader("config/mybatis-config.xml");//对应位置为resources/config目录下
全局配置文件的格式
configuration(配置)
properties(属性) --*
settings(全局设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置) --*
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器) --*
properties属性配置
将应用中的一些常量定义在一个独立的properties文件
resources/database.properties 存储数据库链接相关的配置常量
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///test?serverTimezone=UTC
username=root
password=654321
在核心配置文件引用
<properties resource="database.properties"/> 引入并解析database.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>
<properties resource="database.properties"/>
<environments default="yan">
<environment id="yan">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/> 使用的是OGNL表达式语言,用于获取properties中定义的key为driver的配置值
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/yan/dao/UserMapper.xml"/>
</mappers>
</configuration>
另外定义属性的方法
<properties resource="database.properties">
<property name="password" value="123456"/> 如果database.properties中有password则这个配置无效,否则在其他位置可以通过${password}引用这个配置值
</properties>
编程配置
public SqlSessionFactory build(Reader reader, Properties properties)
setting全局配置
setting会改变 MyBatis 的运行时行为,例如是否使用二级缓存。
引入日志查看所执行的sql语句
logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。可取值为SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING,
1、添加依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、针对log4j进行配置
resources/log4j.properties 名称和位置固定
log4j.rootLogger=DEBUG,console # debug就是需要输出的日志等级,只有大于等于debug的日志信息才进行输出
# 控制台(console)
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%m%n
3、全局配置
mybatis-config.xml
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4、执行操作,则在控制台上查看输出
==> Preparing: select * from t_users
==> Parameters:
<== Total: 4
Log4j
Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIXSyslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
日志级别详解
只有大于等于配置等级的日志信息才进行输出
日志级别 | 描述 |
OFF 关闭 | 最高级别,不输出日志 |
FATAL 致命 | 输出非常严重的可能会导致应用程序终止的错误 |
ERROR 错误 | 输出错误,但应用还能继续运行 |
WARN 警告 | 输出可能潜在的危险状况 |
INFO 信息 | 输出应用运行过程的详细信息 |
DEBUG 调试 | 输出更细致的对调试应用有用的信息 |
TRACE 跟踪 | 输出更细致的程序运行轨迹 |
ALL 所有 | 输出所有级别信息 |
编程使用
private static final Logger logger=Logger.getLogger(UserMapperTest.class); 获取日志记录器对象
在方法中输出日志信息
logger.trace("输出内容"); 其中方法名称对应的就是日志等级
logger.warn("输出内容");
输出格式定义
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
%r 输出自应用启动到输出该log信息耗费的毫秒数
%c 输出所属的类目,通常就是所在类的全名
%t 输出产生该日志事件的线程名
%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。
类型别名typeAliases
解决在映射元文件种多次编写类全名的问题
<select id="load" parameterType="long" resultType="com.yan.entity.UserBean">
select * from t_users where id=#{id}
</select>
定义别名后在映射元文件种则可以使用别名替代全名
mybatis-config.xml
<typeAliases>
<typeAlias type="com.yan.entity.UserBean" alias="User"/> 配置别名User用于替代类全名com.yan.entity.UserBean
</typeAliases>
可以修改映射元文件
<select id="load" parameterType="long" resultType="User">
select * from t_users where id=#{id}
</select>
实际上系统提供了一组别名
别名 | 映射的类型 |
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
类型处理器typeHandlers
MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。
#{birth,jdbcType=DATE}
##对象工厂objectFactory
每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。 如果想覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现。
##插件plugins
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
##环境配置environments
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
<environments default="dev">
<environment id="dev">
<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>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="jdbc:mysql://192.168.3.102:3306/test?serverTimezone=UTC"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<environment id="prod">
<transactionManager type="JDBC"/>
<dataSource type="JNDI">
<property name="data_source " value="java:/aaa"/>
</dataSource>
</environment>
</environments>
事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
- JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
- MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:
数据源(dataSource)
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
- 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):
UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:
-
driver
– 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)。 -
url
– 这是数据库的 JDBC URL 地址。 -
username
– 登录数据库的用户名。 -
password
– 登录数据库的密码。 -
defaultTransactionIsolationLevel
– 默认的连接事务隔离级别。 -
defaultNetworkTimeout
– 等待数据库操作完成的默认网络超时时间(单位:毫秒)。查看java.sql.Connection#setNetworkTimeout()
的 API 文档以获取更多信息。
作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:
driver.encoding=UTF8
这将通过 DriverManager.getConnection(url, driverProperties) 方法传递值为 UTF8
的 encoding
属性给数据库驱动。
POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:
-
poolMaximumActiveConnections
– 在任意时间可存在的活动(正在使用)连接数量,默认值:10 -
poolMaximumIdleConnections
– 任意时间可能存在的空闲连接数。 -
poolMaximumCheckoutTime
– 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒) -
poolTimeToWait
– 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。 -
poolMaximumLocalBadConnectionTolerance
– 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过poolMaximumIdleConnections
与poolMaximumLocalBadConnectionTolerance
之和。 默认值:3(新增于 3.4.5) -
poolPingQuery
– 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。 -
poolPingEnabled
– 是否启用侦测查询。若开启,需要设置poolPingQuery
属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。 -
poolPingConnectionsNotUsedFor
– 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。
JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。这种数据源配置只需要两个属性:
-
initial_context
– 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。 -
data_source
– 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。
和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给 InitialContext。比如:
env.encoding=UTF8
数据库厂商标识databaseIdProvider
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId
属性。
映射器mappers
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:///
形式的 URL),或类名和包名等
映射元文件
-
cache
– 该命名空间的缓存配置。 -
cache-ref
– 引用其它命名空间的缓存配置。 -
resultMap
– 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。 -
sql
– 可被其它语句引用的可重用语句块。 -
insert
– 映射插入语句。 -
update
– 映射更新语句。 -
delete
– 映射删除语句。 -
select
– 映射查询语句。
select
参数类型为long,结果类型为Map
<select id="selectMap" parameterType="long" resultType="map"> 这里的long和map都是系统预定义的别名
select * from t_users where id=#{id}
</select>
修改接口
public interface UserMapper {
Map<String,Object> selectMap(Long id);
}
调用
@Test
public void testSelectMap() {
SqlSession session = factory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
Map<String, Object> res = userMapper.selectMap(1L);
System.out.println(res);
session.close();
}
| 在命名空间中唯一的标识符,可以被用来引用这条语句。对应Mapper接口中的方法名称 |
| 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
| 期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合中元素的类型UserBean,而不是集合本身的类型List。 resultType 和 resultMap 之间只能同时使用一个。一般用于列名称和对象属性名称一致时 |
| 对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。 |
表定义
create table t_users(
id bigint primary key auto_increment,
username varchar(20) not null unique,
password varchar(20) not null,
birth datetime default now(),
sex boolean default 1,
salary numeric(8,2)
)engine=innodb default charset utf8;
实体 类定义
@Data
public class UserBean implements Serializable {
private Long id;
private String username;
private String password;
private Date birth;
private Boolean sex;
private Double salary;
}
引入列名称和属性名称一一对应【名称对应,没有顺序的概念】,所以使用resultType指定返回类型。如果有不对应的内容则返回为null
<select id="load" parameterType="long" resultType="com.yan.entity.UserBean">
select * from t_users where id=#{id}
</select>
假设列名和属性名称不一致,则需要使用resultMap定义对应关系
create table t_users(
id bigint primary key auto_increment,
username varchar(20) not null unique,
password varchar(20) not null,
birth datetime default now(),
sex boolean default 1,
salary numeric(8,2)
)engine=innodb default charset utf8;
实体类定义
@Data
public class UserBean implements Serializable {
private Long id1;
private String username2;
private String password3;
private Date birth4;
private Boolean sex5;
private Double salary6;
}
由于表中的列名称和实体类中的属性名称不一致,所以需要通过resultMap声明对应关系
<resultMap id="userMapper" type="com.yan.entity.UserBean">
<id column="id" property="id1" javaType="long"/>
<result column="username" property="username2" javaType="java.lang.String"/>
<result column="password" property="password3"/>
<result column="birth" property="birth4" jdbcType="DATE"/>
<result column="sex" property="sex5"/>
<result column="salary" property="salary6"/>
</resultMap>
resultMap
元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets
数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap
能够代替实现同等功能的数千行代码。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
resultMap的子标签
-
id
– 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能 -
result
– 注入到字段或 JavaBean 属性的普通结果
子标签的属性
| 映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。 |
| 数据库中的列名,或者是列的别名。一般情况下,这和传递给 |
| 一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
| JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。 |
resultMap的属性
| 当前命名空间中的一个唯一标识,用于标识一个结果映射。 |
| 类的完全限定名, 或者一个类型别名,用于指定返回值的类型。 |
| 如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。 |
引入resultMap
<select id="selectAll" resultMap="userMapper"> 其中userMapper就是<resultMap>的id
select * from t_users
</select>
insert
<insert id="save" parameterType="com.yan.entity.UserBean" useGeneratedKeys="true" keyProperty="id">
insert into t_users(username,password) values(#{username},#{password})
</insert>
在oracle中没有auto_increment,子增长需要依赖sequence序列对象
<insert id="insertUser" parameerType="com.yan.entity.UserBean">
<selectKey keyProperty="id" resultType="long" order="BEFORE"> 对应的属性名为id,对应的类型为long,执行时机为insert语句之前
select seq1.nextval from dual oracle中的sql语句,用于执行查询一个自增长的id值
</selectKey>
insert into t_users(id, username, password) values (#{id}, #{username}, #{password})
</insert>
| 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
| 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
| 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
| (仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。 |
| (仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置( |
| (仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
selectKey 元素的属性
|
|
| 返回结果集中生成列属性的列名。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
| 结果的类型。通常 MyBatis 可以推断出来,但是为了更加准确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果生成列不止一个,则可以使用包含期望属性的 Object 或 Map。 |
| 可以设置为 |
update 和 delete
<delete id="remove" parameterType="long">
delete from t_users where id=#{id}
</delete>
<update id="modify" parameterType="com.yan.entity.UserBean">
update t_users set username=#{username},password=#{password} where id=#{id}
</update>
##sql
这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值
<sql id="cols">
id,username,password,birth,sex,salary
</sql>
<select id="load" parameterType="long" resultType="com.yan.entity.UserBean">
select <include refid="cols"></include> from t_users where id=#{id}
</select>
在日志系统中查看执行的sql语句为
com.yan.dao.UserMapper.load ==> Preparing: select id,username,password,birth,sex,salary from t_users where id=?
com.yan.dao.UserMapper.load ==> Parameters: 3(Long)
com.yan.dao.UserMapper.load <== Columns: id, username, password, birth, sex, salary
com.yan.dao.UserMapper.load <== Row: 3, 小吕2, 266666, 2021-04-19 10:12:28, 1, null
com.yan.dao.UserMapper.load <== Total: 1