1.原生JDBC
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
1.加载
JDBC
驱动程序
2.创建数据库的连接
3.创建
preparedStatement
4.执⾏
SQL
语句
5.处理结果集
6.关闭
JDBC
对象资源
Class.forName("com.mysql.cj.jdbc.Driver");
//连接
String url = "jdbc:mysql://127.0.0.1:3306/test?
useUnicode=true&characterEncoding=utf-8&useSSL=false";
String username = "test";
String password = "test";
//获取连接对象,并连接数据库
Connection connection = DriverManager.getConnection(url,username,password);
//获取语句对象
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from
t_test1");
while (resultSet.next()){
System.out.println(resultSet.toString());
}
statement.close();
每次要加载驱动,连接信息是硬编码
sql
和
java
代码耦合⼀起,改动不易
参数设置缺少灵活性
处理结果集麻烦,不能⾃动映射到实体类
连接资源不能复⽤,资源浪费
ORM对象关系映射框架
对数据库的表和POJO(Plain Ordinary Java Object,普通Java对象 )Java对象的做映射的框架hibernate (ssh)
笨重, sql 调优麻烦JPA - Spring Data JPA
Java Persistence API, 轻量级,部分中⼩项⽬适合mybatis
半⾃动化 ( 半 ORM 框架 ) ,便于写 sql ,轻量级,⼤⼚⼴泛使⽤2.MyBatis
mybatis – MyBatis 3 | Introduction
依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!-- 使⽤JDBC链接mysql的驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
</dependencies>
每个基于 MyBatis
的应⽤都是以⼀个
SqlSessionFactory
的实例为核⼼
SqlSessionFactory 的实例可以通过
SqlSessionFactoryBuilder
获得
SqlSessionFactoryBuilder 可以从
XML
配置⽂件或⼀个预先配置的
Confifiguration
实例来构
建出
SqlSessionFactory 实例 ⼯⼚设计模式⾥⾯ 需要获取
SqlSession
,⾥⾯提供了在数据库执⾏
SQL 命令所需的所有⽅法
配置
mybatis-confifig.xml.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://127.0.0.1:3306/test2?
useUnicode=true&characterEncoding=utf-8&useSSL=false"/>
<property name="username" value="test"/>
<property name="password" value="test"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/TestMaper.xml"/>
</mappers>
</configuration>
TestMapper.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.test.dao.TestMapper">
<select id="selectById"
resultType="com.test.domain.Test">
select * from t_test_2 where id = #{test_id}
</select>
</mapper>
取
java
对象的某个值,属性名⼤⼩写要⼀致
#{value} : 推荐
,
是
java
的名称
${value} : 不推荐,存在
sql
注⼊⻛险
代码方式 补⻬对象字段Main.java
public static void main(String [] args) throws IOException {
String resouce = "config/mybatis-config.xml";
//读取配置⽂件
InputStream inputStream = Resources.getResourceAsStream(resouce);
//构建Session⼯⼚
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取Session
try(SqlSession sqlSession = sqlSessionFactory.openSession()){
TestMapper testMapper =
sqlSession.getMapper(TestMapper.class);
Test test= testMapper.selectById(1);
List<Test > testList = testMapper.selectList();
System.out.println(testList .toString());
}
}
注解
/**
* 查询列表
* @return
*/
@Select("select * from t_test2")
List<Test> selectList();
Mybatis使⽤流程
创建 mybatis-config.xml 全局的配置⽂件 创建 XXXMapper.xml 配置⽂件 创建 SqlSessionFactory ⽤ SqlSessionFactory 创建 SqlSession 对象 ⽤ SqlSession 执⾏增删改查 CRUD使⽤log4j配置打印sql
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
在应⽤的classpath中创建名称为 log4j.properties 的⽂件
log4j.rootLogger=ERROR, stdout
log4j.logger.com.test=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
mysql⾃带函数使⽤(where ... and like ..)
<select id="selectByPointAndTitleLike"
resultType="com.test.domain.Test">
select * from t_test2 where point=#{point} and title like concat('%', #
{title},'%')
</select>
Mybatis 配置驼峰字段映射java对象和数据库字段
Mybatis ⾃带配置 ( 加在⽂件顶部 ) <!--下划线⾃动映射驼峰字段-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
Mybatis⼊参parameterType和取值类型
parameterType 参数类型 可以是基本类型 可以是 Java 集合 List 或者 Map 可以是 Java ⾃定义对象 取参数值,具体某个字段的类型,从 java类型映射到数据库类型.例⼦ #{title, jdbcType=VARCHAR} 注意: 多数情况不加是正常使⽤,但是如果出现报错:⽆效的列类型,则是缺少 jdbcType; 只有当字段可为 NULL 时才需要 jdbcType 属性 常⻅的数据库类型和 java 列席对⽐JDBC Type Java Type
CHAR String
VARCHAR String
LONGVARCHAR String
NUMERIC java.math.BigDecimal
DECIMAL java.math.BigDecimal
BIT boolean
BOOLEAN boolean
TINYINT byte
SMALLINT short
INTEGER INTEGER
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
新增数据
<insert id="add" parameterType="com.test.domain.Test">
INSERT INTO `t_test2` ( `title`, `summary`, `cover_img`, `price`,
`create_time`, `point`)
VALUES
(#{title,jdbcType=VARCHAR},#{summary,jdbcType=VARCHAR},#
{coverImg,jdbcType=VARCHAR},#{price,jdbcType=INTEGER},
#{createTime,jdbcType=TIMESTAMP},#{point,jdbcType=DOUBLE});
</insert>
获得插⼊的⾃增主键
<insert id="add" parameterType="com.test.domain.Test"
useGeneratedKeys="true" keyProperty="id" keyColumn="id" >
INSERT INTO `t_test2` ( `title`, `summary`, `cover_img`, `price`,
`create_time`, `point`)
VALUES
(#{title,jdbcType=VARCHAR},#{summary,jdbcType=VARCHAR},#
{coverImg,jdbcType=VARCHAR},#{price,jdbcType=INTEGER},
#{createTime,jdbcType=TIMESTAMP},#{point,jdbcType=DOUBLE});
</insert>
//新增⼀条记录
Test test= new Test();
test.setTitle("testtesttest");
test.setCoverImg("xdclass.net/aaa.png");
test.setPoint(9.4);
test.setCreateTime(new Date());
test.setPrice(9900);
test.setSummary("test for test");
int rows = testMapper.add(test);
System.out.println(rows);
System.out.println(test.toString());
Mybatis实战foreach批量插⼊语法
foreach: ⽤于循环拼接的内置标签,常⽤于 批量新增、 in 查询等常⻅包含以下属性:
collection:必填,值为要迭代循环的集合类型,情况有多种
⼊参是List类型的时候,collection属性值为list
⼊参是Map类型的时候,collection 属性值为map的key值
item:每⼀个元素进⾏迭代时的别名
index:索引的属性名,在集合数组情况下值为当前索引值,当迭代对象是map时,这个值是
map的key
open:整个循环内容的开头字符串
close:整个循环内容的结尾字符串
separator: 每次循环的分隔符
<!--批量插⼊-->
<insert id="addBatch"
parameterType="com.test.domain.Test"
useGeneratedKeys="true" keyProperty="id" keyColumn="id" >
INSERT INTO `t_test2` ( `title`, `summary`, `cover_img`, `price`,
`create_time`, `point`)
VALUES
<foreach collection="list" item="test" separator=",">
(#{test.title,jdbcType=VARCHAR},#{test.summary,jdbcType=VARCHAR},#
{test.coverImg,jdbcType=VARCHAR},
#{test.price,jdbcType=INTEGER},
#{test.createTime,jdbcType=TIMESTAMP},#{test.point,jdbcType=DOUBLE})
<foreach/>
MyBatis3.X更新
<update id="updateTest"
parameterType="com.test.domain.Test">
UPDATE t_test2
set
title = #{title,jdbcType=VARCHAR},
summary = #{summary,jdbcType=VARCHAR},
cover_img = #{coverImg,jdbcType=VARCHAR},
price = #{price,jdbcType=INTEGER},
c_id = #{cId,jdbcType=INTEGER},
point = #{point,jdbcType=INTEGER},
update_time = now()
WHERE
id = #{id}
</update>
存在其他不想被更新的字段却置为
null
或者默认值了
Mybatis 动态字段更新 if test 标签
//当前字段符合条件才更新这个字段的值
<if test='title != null and id == 1'> title = #{title}, </if>
<if test="title!=null"> title = #{title}, </if>
<update id="updateTestSelective"
parameterType="com.test.domain.Test">
update t_test2
<trim prefix="set" suffixOverrides=",">
<if test="title != null "> title = #{title,jdbcType=VARCHAR},
</if>
<if test="summary != null "> summary = #
{summary,jdbcType=VARCHAR},</if>
<if test="coverImg != null "> cover_img = #
{coverImg,jdbcType=VARCHAR},</if>
<if test="price != 0 "> price = #{price,jdbcType=INTEGER},
</if>
<if test="createTime !=null "> create_time = #
{createTime,jdbcType=TIMESTAMP},</if>
<!-- 特别注意: ⼀定要看pojo类⾥⾯的是基本数据类型,还是包装数据类型-->
<if test="point != null "> point = #{point,jdbcType=DOUBLE},
</if>
</trim>
where
id = #{id}
</update>
解Mybatis删除语法操作
<delete id="deleteByCreateTimeAndPrice" parameterType="java.util.Map">
delete from test where create_time <![CDATA[ > ]]> #{createTime}
and price <![CDATA[ >= ]]> #{price}
</delete>
⼤于等于
<![CDATA[ >= ]]>
⼩于等于
<![CDATA[ <= ]]>
3.mybatis-confifig.xml常⻅属性
核⼼配置⽂件(dom节点顺序要求,不然报错)
configuration (配置) properties (属性) settings (设置) typeAliases (类型别名) typeHandlers (类型处理器) objectFactory (对象⼯⼚) plugins (插件,少⽤) environments (环境配置,不配多环境,基本在 Spring ⾥⾯配置) environment (环境变量) transactionManager (事务管理器) dataSource (数据源) databaseIdProvider (数据库⼚商标识) mappers (映射器) mybatis – MyBatis 3 | 配置MyBatis3.X 查询typeAlias别名
typeAlias 类型别名,给类取个别名,可以不⽤输⼊类的全限定名 <typeAlias type="com.test.domain.Test"
alias="Test"/>
<!--<select id="selectById" parameterType="java.lang.Integer"
resultType="com.test.domain.Test">-->
<select id="selectById" parameterType="java.lang.Integer"
resultType="Test">
select * from t_test2 where id = #{test_id,jdbcType=INTEGER}
</select>
<typeAliases>
<!--<typeAlias type="com.test.domain.Test"
alias="Test"/>-->
<package name="com.test.domain"/>
</typeAliases>
Mybatis的sql⽚段
<sql id="base_test_field">
id,title,summary,cover_img
</sql>
<select id="selectById" parameterType="java.lang.Integer"
resultType="Test">
select <include refid="base_test_field"/> from t_test2 where
id = # {test_id,jdbcType=INTEGER}
</select>
<select id="selectListByXML" resultType="Test">
select <include refid="base_test_field"/> from t_test2
</select>
4.复杂Sql查询
Mybatis的resultMap
resultType 查询出的字段在相应的pojo 中必须有和它相同的字段对应,或者基本数据类型 适合简单查询 resultMap 需要⾃定义字段,或者多表查询,⼀对多等关系,⽐resultType 更强⼤ 适合复杂查询<resultMap id="TestResultMap" type="Test">
<!--
id 指定查询列的唯⼀标示
column 数据库字段的名称
property pojo类的名称
-->
<id column="id" property="id" jdbcType="INTEGER" />
<result column="test_tile" property="title" jdbcType="VARCHAR" />
<result column="summary" property="summary" jdbcType="VARCHAR" />
<result column="cover_img" property="coverImg" jdbcType="VARCHAR" />
</resultMap>
<select id="selectBaseFieldByIdWithResultMap" resultMap="TestResultMap">
select id , title as test_tile, summary, cover_img from t_test2 where id = #
{test_id}
</select>
复杂对象映射配置ResultMap的association
<resultMap id="orderResultMap" type="Order">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="out_trade_no" property="outTradeNo"/>
<result column="create_time" property="createTime"/>
<result column="state" property="state"/>
<result column="total_fee" property="totalFee"/>
<result column="test_id" property="testId"/>
<result column="test_title" property="testTitle"/>
<result column="test_img" property="testImg"/>
<!-- association 配置属性⼀对⼀
property 对应videoOrder⾥⾯的user属性名
javaType 这个属性的类型
-->
<association property="user" javaType="User">
<id property="id" column="user_id"/>
<result property="name" column="name"/>
<result property="headImg" column="head_img"/>
<result property="createTime" column="create_time"/>
<result property="phone" column="phone"/>
</association>
</resultMap>
<!--⼀对⼀管理查询订单, 订单内部包含⽤户属性-->
<select id="queryVideoOrderList" resultMap="OrderResultMap">
select
o.id id,
o.user_id ,
o.out_trade_no,
o.create_time,
o.state,
o.total_fee,
o.test_id,
o.test_title,
o.test_img,
u.name,
u.head_img,
u.create_time,
u.phone
from order o left join user u on o.user_id = u.id
</select>
// resultmap association关联查询
OrderMapper orderMapper =
sqlSession.getMapper(OrderMapper.class);
List<Order> orderList = orderMapper.queryVideoOrderList();
System.out.println(orderList.toString());
⼀对多映射配置ResultMap的collection
<resultMap id="UserOrderResultMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="headImg" column="head_img"/>
<result property="createTime" column="create_time"/>
<result property="phone" column="phone"/>
<!--
property 填写pojo类中集合类属性的名称
ofType 集合⾥⾯的pojo对象
-->
<collection property="orderList" ofType="Order">
<!--配置主键,管理order的唯⼀标识-->
<id column="order_id" property="id"/>
<result column="user_id" property="userId"/>
<result column="out_trade_no" property="outTradeNo"/>
<result column="create_time" property="createTime"/>
<result column="state" property="state"/>
<result column="total_fee" property="totalFee"/>
<result column="video_id" property="videoId"/>
<result column="video_title" property="videoTitle"/>
<result column="video_img" property="videoImg"/>
</collection>
</resultMap>
<select id="queryUserOrder" resultMap="UserOrderResultMap">
select
u.id,
u.name,
u.head_img,
u.create_time,
u.phone,
o.id order_id,
o.out_trade_no,
o.user_id,
o.create_time,
o.state,
o.total_fee,
o.test_id,
o.test_title,
o.test_img
from user u left join order o on u.id = o.user_id
</select>
// resultmap association关联查询
OrderMapper orderMapper =
sqlSession.getMapper(OrderMapper.class);
//resultmap collection测试
List<User> userList = orderMapper.queryUserOrder();
System.out.println(userList.toString());
association
映射的是⼀个
pojo
类,处理⼀对⼀的关联关系。
collection
映射的⼀个集合列表,处理的是⼀对多的关联关系。
模板
<!-- column不做限制,可以为任意表的字段,⽽property须为type 定义的pojo属性-->
<resultMap id="唯⼀的标识" type="映射的pojo对象">
<id column="表的主键字段,或查询语句中的别名字段" jdbcType="字段类型" property="映射
pojo对象的主键属性" />
<result column="表的⼀个字段" jdbcType="字段类型" property="映射到pojo对象的⼀个属
性"/>
<association property="pojo的⼀个对象属性" javaType="pojo关联的pojo对象">
<id column="关联pojo对象对应表的主键字段" jdbcType="字段类型" property="关联pojo
对象的属性"/>
<result column="表的字段" jdbcType="字段类型" property="关联pojo对象的属性"/>
</association>
<!-- 集合中的property 需要为oftype定义的pojo对象的属性-->
<collection property="pojo的集合属性名称" ofType="集合中单个的pojo对象类型">
<id column="集合中pojo对象对应在表的主键字段" jdbcType="字段类型" property="集合
中pojo对象的主键属性" />
<result column="任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属
性" />
</collection>
</resultMap>
5.多级缓存与懒加载
Mybatis⼀级缓存
⼀级缓存的作⽤域是 SQLSession ,同⼀个 SqlSession 中执⾏相同的 SQL 查询 (相同的SQL 和参数 ),第⼀次会去查询数据库并写在缓存中,第⼆次会直接从缓存中取; 基于 PerpetualCache 的 HashMap本地缓存; 默认开启⼀级缓存; 失效策略:当执⾏ SQL 时候两次查询中间发⽣了增删改的操作,即 insert 、 update 、 delete 等操作 commit 后会清空该 SQLSession 缓存 ; ⽐如 sqlsession 关闭,或者清空等;Mybatis⼆级缓存
⼆级缓存是 namespace 级别的,多个 SqlSession 去操作同⼀个 namespace下的Mapper 的 sql 语句,多个 SqlSession 可以共⽤⼆级缓存 , 如果两个 mapper 的 namespace相同,(即使是两个 mapper ,那么这两个 mapper 中执⾏ sql查询到的数据也将存在相同的⼆级缓存区域中,但是最后是每个 Mapper 单独的名称空间); 基于 PerpetualCache 的 HashMap 本地缓存,可⾃定义存储源,如 Ehcache/Redis 等; 默认是没有开启⼆级缓存; 操作流程:第⼀次调⽤某个 namespace 下的 SQL去查询信息,查询到的信息会存放该 mapper 对应的⼆级缓存区域。 第⼆次调⽤同个 namespace 下的 mapper映射⽂件中,相同的 sql 去查询信息,会去对应的⼆级缓存内取结果; 失效策略:执⾏同个 namespace 下的 mapepr 映射⽂件中增删改 sql ,并执⾏了 commit 操作 ,会清空该⼆级缓存 注意:实现⼆级缓存的时候, MyBatis 建议返回的 POJO是可序列化的, 也就是建议实现 Serializable 接⼝ 缓存淘汰策略:会使⽤默认的 LRU 算法来收回(最近最少使⽤的) 如何开启某个⼆级缓存 mapper.xml ⾥⾯配置<!--开启mapper的namespace下的⼆级缓存-->
<!--
eviction:代表的是缓存回收策略,常⻅下⾯两种。
(1) LRU,最近最少使⽤的,⼀处最⻓时间不⽤的对象
(2) FIFO,先进先出,按对象进⼊缓存的顺序来移除他们
flushInterval:刷新间隔时间,单位为毫秒,这⾥配置的是100秒刷新,如果不配置
它,当SQL被执⾏的时候才会去刷新缓存。
size:引⽤数⽬,代表缓存最多可以存储多少个对象,设置过⼤会导致内存溢出
readOnly:只读,缓存数据只能读取⽽不能修改,默认值是false
-->
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>
全局配置:
<settings>
<!--这个配置使全局的映射器(⼆级缓存)启⽤或禁⽤缓存,全局总开关,这⾥关闭,mapper中开启
了也没⽤-->
<setting name="cacheEnabled" value="true" />
</settings>
如果需要控制全局
mapper
⾥⾯某个⽅法不使⽤缓存,可以配置
useCache="false"
<select id="selectById" parameterType="java.lang.Integer"
resultType="Test" useCache="false">
select <include refid="base_test_field"/> from t_test2 where id = #
{test_id,jdbcType=INTEGER}
</select>
优先查询⼆级缓存
-
》查询⼀级缓存
-
》数据库
什么是懒加载: 按需加载,先从单表查询,需要时再从关联表去关联查询,能⼤⼤提⾼数据库性能
,
并不是所有场景下使⽤懒加载都能提⾼效率
Mybatis
懒加载:
resultMap
⾥⾯的
association
、
collection
有延迟加载功能
<!--全局参数设置-->
<settings>
<!--延迟加载总开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--将aggressiveLazyLoading设置为false表示按需加载,默认为true-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<resultMap id="TestOrderResultMapLazy" type="TestOrder">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="out_trade_no" property="outTradeNo"/>
<result column="create_time" property="createTime"/>
<result column="state" property="state"/>
<result column="total_fee" property="totalFee"/>
<result column="test_id" property="testId"/>
<result column="test_title" property="testTitle"/>
<result column="test_img" property="testImg"/>
<!--
select: 指定延迟加载需要执⾏的statement id
column: 和select查询关联的字段
-->
<association property="user" javaType="User" column="user_id"
select="findUserByUserId"/>
</resultMap>
<!--⼀对⼀管理查询订单, 订单内部包含⽤户属性 懒加载-->
<select id="queryTestOrderListLazy" resultMap="TestOrderResultMapLazy">
select
o.id id,
o.user_id ,
o.out_trade_no,
o.create_time,
o.state,
o.total_fee,
o.test_id,
o.test_title,
o.test_img
from test_order o
</select>
<select id="findUserByUserId" resultType="User">
select * from user where id=#{id}
</select>
dubug
模式测试懒加载不准确,可以直接
run
6.MyBatis3.x 的事务管理形式
使⽤JDBC的事务管理
使⽤ java.sql.Connection 对象完成对事务的提交( commit() )、回滚( rollback())、关闭( close() )使⽤MANAGED的事务管理
MyBatis ⾃身不会去实现事务管理,⽽让程序的容器如( Spring, JBOSS)来实现对事务的管理<environment id="development">
<!-- mybatis使⽤jdbc事务管理⽅式 -->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://127.0.0.1:3306/test?
useUnicode=true&characterEncoding=utf-8&useSSL=false"/>
<property name="username" value="test"/>
<property name="password" value="test"/>
</dataSource>
</environment>
事务⼯⼚
TransactionFactory
的两个实现类
JdbcTransactionFactory->JdbcTransaction
ManagedTransactionFactory->ManagedTransaction
注意:如果不是
web
程序,然后使⽤的事务管理形式是
MANAGED,
那么将没有事务管理功能