已更新面试技术总结
技术方向
必知
1.#{}和${}的区别是什么(面试会问)
#{}是预编译处理
eg:select * from user where reg_tms= #{reg_tms} ※#{reg_tms} 是sql语句中的'?'号,调用PreparedStatement 的set 方法来赋值
${}是字符串替换
eg:order by ${user} ※注意:${}不能防止sql注入
2. Mybatis 是如何进行分页的?
使用RowBounds对象进行分页,根据ResultSet返回的结果集进行分页,并不是物理内存分页
3. Mybatis 分页插件的原理是什么?(面试会问)
拦截器首先执行完重写sql语句,根据dialect进行详细的分页处理
源码分析
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.8</version>
</dependency>
@RequestMapping("/listCategory")
public String listCategory(Model m, @RequestParam(value = "start", defaultValue = "0") int start, @RequestParam(value = "size", defaultValue = "5") int size) throws Exception {
PageHelper.startPage(start,size,"id desc");
List<Category> cs=categoryServiceImpl.findAll();
PageInfo<Category> page = new PageInfo<>(cs);
m.addAttribute("page", page);
return "listCategory";
}
public class PageInterceptor implements Interceptor {
// 重写sql语句,根据dialect进行详细的分页处理
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 省略
}
}
/**
* 开始分页
*
* @param pageNum 页码
* @param pageSize 每页显示数量
* @param orderBy 排序
*/
public static <E> Page<E> startPage(int pageNum, int pageSize, String orderBy) {
Page<E> page = startPage(pageNum, pageSize);
page.setOrderBy(orderBy);
return page;
}
/**
* 开始分页
*
* @param pageNum 页码
* @param pageSize 每页显示数量
* @param count 是否进行count查询
* @param reasonable 分页合理化,null时用默认配置
* @param pageSizeZero true且pageSize=0时返回全部结果,false时分页,null时用默认配置
*/
public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
Page<E> page = new Page<E>(pageNum, pageSize, count);
page.setReasonable(reasonable);
page.setPageSizeZero(pageSizeZero);
//当已经执行过orderBy的时候
Page<E> oldPage = getLocalPage();
if (oldPage != null && oldPage.isOrderByOnly()) {
page.setOrderBy(oldPage.getOrderBy());
}
setLocalPage(page);
return page;
}
4.Mybatis的映射形式
<resultMap>标签
定义数据库列名和对象属性名之间的映射关系
<resultType>标签
将列的别名书写为对象属性名
5.Mybatis 动态SQL(重点,面试会问)
<choose>
判断参数
<where> <choose> <when test="isUpdate !=null "> AND user=#{user} </when> </choose> </where>
<trim>
拼接关键字或去除多余字符
1.prefix
给sql语句拼接的前缀
2.suffix
给sql语句拼接的后缀
3.prefixOverrides
去除sql语句前面的关键字或字符
4.suffixOverrides
去除sql语句后面的关键字或字符
5.例子
<!-- 传入的state为空,title不为空 --> <trim prefix="where" prefixOverrides="and"> <if test="state != null"> state = #{state} </if> <if test="title != null"> and title = #{title} </if> </trim>
sql语句:where state = ?
<where>
匹配条件
<set>
设置
<update > <set> <if test="user!=null"> user =#{user}, </if> </set> </update>
具体作用
默认忽视逗号
<foreach>
循环
1.collection
返回类型
1_1.1 list
List<User> getUsers();
<select id="getUsers" resultType="User"> select * from user where user_id in <foreach collection="list" item="item" open="(" separator="," close=")" index="index"> #{item} </foreach> </select>
select * from user where user_id in (?,?...?)
1_1.2 array
int delUsers(String[] arr);
<delete id="delUsers"> delete from user where user_id in <foreach collection="array" item="item" open="(" separator="," close=")" index="index"> #{item} </foreach> </delete >
delete from user where user_id in (?,?...?)
1_1.3 ids
List<User> getUsers();
<select id="getUsers" parameterType="java.util.Map" resultType="User"> select * from user where user_name = #{name} and user_id in <foreach collection="ids" item="item" open="(" separator="," close=")" index="index"> #{item} </foreach> </select>
select * from user where user_name = ? and user_id in (?,?...?)
1_1.4 对象
int addUsers(User user);
<insert id="addUsers" parameterType="User" > insert into tab_user (user_id,user_name) values <foreach collection="userId.split(',')" item="item" separator=","> (#{item},#{userName}) </foreach> </insert>
insert into tab_user (user_id,user_name) values (?,?);
2.index
索引(可选)
3.item
别名(必选)
4.open
开始符号(可选)
5.separator
分隔符(可选)
6.close
结束符号(可选)
<otherwise>
否则
<where> <choose> <when test="isUpdate !=null "> AND user=#{user} </when> <otherwise> and user ='1' </otherwise> </choose> </where>
<bind>
绑定
<!--使用bind元素进行模糊查询--> <select id="selectUserByBind" resultType="com.po.MyUser" parameterType= "com.po.MyUser"> <!-- bind 中的 uname 是 com.po.MyUser 的属性名--> <bind name="paran_uname" value="'%' + uname + '%'"/> select * from user where uname like #{paran_uname} </select>
select * from user where uname like ('%?%') <!-- 防止模糊查询sql注入 -->
6.标签
<select>
1.id
唯一标识符,映射bean的方法名
List<Student> getAll();
<select id="getAll" ></select>
2.parameterType
参数类型
Student selectByPrimaryKey(Integer id);
<select id="selectByPrimaryKey" parameterType="java.lang.Integer"></select>
3.resultType
返回值类型
int countUser();
<select id="countUser" resultType="int"></select>
4.resultMap
返回映射值
public class Student{ private Integer id; private String name; } // mapper映射方法 Student selectByPrimaryKey(Integer id);
// 将返回的结果bean映射成键值对 <resultMap id="BaseResultMap" type="Student"> <id column="id" property="id" /> <!-- column:映射student表里的字段name, jdbcType:映射student的name参数类型,property:映射Student.java里的参数name--> <result column="name" jdbcType="VARCHAR" property="name" /> </resultMap> <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap"> </select>
<insert>
<insert id="insert" parameterType="Student"></insert>
<updae>
<update id="update" parameterType="Student"></update>
<delete>
<delete id="delete" parameterType="java.lang.Integer"></delete>
<sql>和<include>
Student selectByPrimaryKey(Integer id);
<sql id="Base_Column_List"> id, name, sex, age, score </sql> <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from student where id = #{id,jdbcType=INTEGER} </select>
<selectKey>
int insert(Student record);
<insert id="insert" parameterType="Student"> <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> SELECT LAST_INSERT_ID() </selectKey> insert into student (name,) values (#{name,jdbcType=VARCHAR}) </insert>
<constructor>
构造方法
public class Student{ private Integer id; private String name; get/set省略 public Student(Integer id, String name, User user) { super(); this.id = id; this.name = name; this.user = user; } } List<Student> findStudentAll();
<resultMap id="BaseResultMap" type="Student"> <constructor> <idArg column="id" javaType="Integer"/> <arg column="name" javaType="String"/> <!-- typeHandler:映射的bean --> <arg column="user" javaType="User" typeHandler="User"/> </constructor> </resultMap> <select id="findStudentAll" resultMap="BaseResultMap" > select * from student; </select>
<association>
一对一关联查询
public class Person { private Integer id; private String name; private User user; // 省略setter和getter方法 } public class User { private Integer id; private String code; // 省略setter和getter方法 } Person selectCodeById(Integer id);
<select id="selectCodeById" parameterType="Integer" resultType= "User> select * from user where id=#{id} </select> <resultMap type="Person" id="BaseResultMap"> <id property="id" column="id"/> <result property="name" column="name"/> <!-- 一对一级联查询--> <association property="user" column="user_id" javaType="User" select="selectCodeByld"/> </resultMap> <select id="selectPersonById" parameterType="Integer" resultMap= "BaseResultMap"> select * from person where id=#{id} </select>
<collection>
一对多关联查询
public class Person { private Integer id; private String name; private List<User> users; // 省略setter和getter方法 } public class User { private Integer id; private String code; // 省略setter和getter方法 } Person selectCodeById(Integer id);
<select id="selectCodeById" parameterType="Integer" resultType= "User> select * from user where id=#{id} </select> <resultMap type="Person" id="BaseResultMap"> <id property="id" column="id"/> <result property="name" column="name"/> <!-- 一对多级联查询--> <collection property="users" column="users" ofType="User" select="selectCodeByld"/> <!-- <collection property="users" ofType="User"> <id property="id" column="id" /> <result property="code" column="code" /> </collection> --> </resultMap> <select id="selectPersonById" parameterType="Integer" resultMap= "BaseResultMap"> select * from person where id=#{id} </select>
<discriminator>
鉴别器/分类器:分别同样的子类,返回不同信息
数据库表字段 id int(11) NO PRI auto_increment name varchar(50) YES sex varchar(2) YES age varchar(10) YES type varchar(50) YES id | name | sex | age | type 0 | tom | 男 | 20 | 1 1 | kit | 女 |18 | 2
public class Person { private Integer id; private String name; private String sex; private String age; // 省略setter和getter方法 } public class Student extends Person{ private Integer id; private String name; private String age; // 省略setter和getter方法 } public class User extends Person{ private Integer id; private String name; private String sex; // 省略setter和getter方法 } Person selectPersonById(Integer id);
<resultMap type="Person" id="BaseResultMap"> <id property="id" column="id"/> <result property="name" column="name"/> <result property="sex" column="sex"/> <result property="age" column="age"/> <!-- 根据type判断返回什么对象 --> <discriminator javaType="int" column="type"> <case value="1" resultType="Student"> <id column="id" property="id"/> <result column="name" property="name"/> <result column="age" property="age"/> </case> <case value="2" resultType="User"> <id column="id" property="id"/> <result column="name" property="name"/> <result column="sex" property="sex"/> </case> </discriminator> </resultMap> <select id="selectPersonById" parameterType="Integer" resultMap="BaseResultMap"> select * from person where id=#{id} </select>
# 结果 select * from person WHERE `id` = ?; Student{id='0',name='tom',age='20'} select * from person WHERE `id` = ?; User{id='1',name='kit',sex='女'}
7.Mybatis延迟加载原理(面试会问)
package org.apache.ibatis.executor.loader.cglib;
public class CglibProxyFactory implements ProxyFactory {
// 创建代理对象
crateProxy()
// 代理对象执行
invoke()
// 主要执行逻辑 ,通过synchronized 判断mybatis的配置为true
// 延迟加载数量大于0
if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
// aggressive一对一关联查询进入
if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
// 对象返回
lazyLoader.loadAll();
} else if (PropertyNamer.isSetter(methodName)) {
// set 标签不需要执行延迟加载
final String property = PropertyNamer.methodToProperty(methodName);
lazyLoader.remove(property);
} else if (PropertyNamer.isGetter(methodName)) {
final String property = PropertyNamer.methodToProperty(methodName);
if (lazyLoader.hasLoader(property)) {
lazyLoader.load(property);
}
}
}
}
public class Person {
private List<User> users;
// 省略setter和getter方法
}
public class User {
}
使用CGLib代理模式,创建代理对象,进入intercept()方法,当匹配方法名一致,Person.getUsers()返回null,把事先保存的sql关联查询出User对象,在调用Person.setUsers(users),最后返回结果。
8.基础配置(面试会问)
<?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="multipleResultSetsEnabled" value="true"></setting>
<!-- 使用jdbc的getGeneratedKeys获取数据库自增主键值 -->
<setting name="useGeneratedKeys" value="true" />
<!-- 使用列别名替换列名 默认:true -->
<setting name="useColumnLabel" value="true" />
<!-- 开启驼峰命名转换:Table{create_time} -> Entity{createTime} -->
<setting name="mapUnderscoreToCamelCase" value="true" />
<!--二级缓存开关-->
<setting name="cacheEnabled" value="true"></setting>
<!--允许返回多个结果集-->
<setting name="multipleResultSetsEnabled" value="true"></setting>
<!--日志-->
<setting name="logImpl" value="LOG4J"></setting>
<!-- 延迟加载总开关( 查询时,关闭关联对象即时加载以提高性能) -->
<setting name="lazyLoadingEnabled" value="false"/>
<!-- 侵入懒加载,设置为false则按需加载,否则会全部加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
<!--设置一个时限,以决定让驱动器等待数据库回应的多长时间为超时-->
<setting name="defaultStatementTimeout" value="25000" />
</settings>
<!-- mybatis分页插件 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
</configuration>
扩展
maven中spring+springmvc+mybatis整合详细配置
9.Executor执行器(面试会问)
SimpleExecutor
每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象
ReuseExecutor
执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map
BatchExecutor
完成批处理
10.Mybatis一级和二级缓存
一级缓存
使用session,默认打开一级缓存且不能关闭。
一级缓存是SqlSession级别的数据,作用域是SqlSession范围的。一个查询产生的一个SqlSession。在同一个SqlSession中执行两次相同的SQL语句时,第一次执行完的数据库中查询的数据斜倒缓冲中,第二次查询是从缓存中获取数据,不再去底层数据库查询,从而提高性能。
注意:为了避免脏数据的出现。(在执行了增删改操作后,mybatis会清空sqlsession中的一级缓存)
二级缓存
默认不打开二级缓存,要手动开启二级缓存
二级缓存是mapper级别的缓存,在多个SqlSession使用一个mapper时,在对数据库查询操作时,利用二级缓存来提高查询性能。
不同的SqlSession两次执行相同的namespace下的SQL语句,切向SQL中传递的参数也相同 ,第二次执行的数据是在缓存中获取。
11.Mybatis是什么(面试会问)
Mybatis是一个半ORM(对象关系映射)框架
a.封装JDBC,开发人员不需要关心sql语句,原生的statement不需要程序员管控
b. 使用XML或注解映射Dao,将POJO映射成数据库中的记录
总结:可以自定义sql、存储过程和高级映射的持久层框架
12.mapper创建原理 (面试会问)
JDK动态代理+工厂模式
创建一个SqlSessionFactory对象读取mybatis相关配置,然后使用session对象调用getMapper反射到对应的bean和sql。getMapper利用jdk代理模式返回任何对象,遇到特殊的方法名进入CRUD装配调用。
源码解析
public void save(User u) throws IOException {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession session = factory.openSession();
// 传入要解析的bean,此bean会自动映射xml文件或注解
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.save(u);
session.commit();
session.close();
}
package org.apache.ibatis.session.defaults
DefaultSqlSession.java
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
package org.apache.ibatis.session;
Configuration.java
/**
* type:类型
* sqlsession:主要配置,连接jdbc的用户和密码,外部扩展
*/
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
package org.apache.ibatis.binding;
MapperRegistry.java
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 最终的读取出来的映射数据类型,statement的各种设置
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// jdk动态代理:生成一个实体类
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
package org.apache.ibatis.binding;
MapperProxyFactory.java
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
// 检查public和非空判断,反射执行,深拷贝了Proxy对象 ,mapperProxy被代理对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
// 构造一个MapperProxy对象
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
package org.apache.ibatis.binding;
MapperProxy.java
// jdk动态代理必须实现InvocationHandler
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
// 然后重写此方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 任何方法名都可以代理生成对象
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 缓存方法
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 判断sql执行类型
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
throws Throwable {
final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
.getDeclaredConstructor(Class.class, int.class);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
final Class<?> declaringClass = method.getDeclaringClass();
return constructor
.newInstance(declaringClass,
MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
}
/**
* Backport of java.lang.reflect.Method#isDefault()
*/
private boolean isDefaultMethod(Method method) {
return (method.getModifiers()
& (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
&& method.getDeclaringClass().isInterface();
}
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional() &&
(result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
13.Mybatis优点/缺点
优点
减少重复的轮子
兼容主流数据库
扩展性高
缺点
更换数据库成本很高
SQL语句要求很高
14.Mybatis三大核心对象以及作用域(面试会问)
三大对象:
1、SqlSessionFactoryBuilder:负责构建SqlSessionFactory,并且提供了多个build()方法的重载
2、SqlSessionFactory:创建SqlSession实例的工厂
3、SqlSession:用于执行持久化操作的对象
生命周期以及作用域:
SqlSessionFactoryBuilder:用过即丢,创建SqlSessionFactory对象后该方法就不存在,所以该方法的范围是在方法体内部
SqlSessionFactory:整个运行过程中都需要,作用域Application
SqlSession:在每次访问数据库时都需要它,作用域:request作用域或者方法体作用域
15.扫描package方式(面试会问)
第一种方式
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>
<mappers>
<!-- 调用dao层 -->
<mapper resource="org/lisonglin/mapper/StudentMapper.xml"/>
</mappers>
</configuration>
第二种方式
application.properties
#SQL语句映射文件
#mybatis.mapper-locaitons= classpath*:com/example/mapper/*.xml
mybatis.mapper-locations=classpath*:org/lanqiao/mapper/*.xml
# 类的别名的包
mybatis.type-aliases-package=org.lanqiao.model
16.SqlSession相关API(面试会问)
- SqlSession是一个面向用户的接口,接口中定义了操作数据库的方法(selectOne、selectList、insert、update、delete)。
- SqlSession的实现类是线程不安全的,所以SqlSession不能定义成局部变量来使用,最佳的使用场合是在一个方法体内(即:作为一个局部变量来使用)。
SqlSession实例,包括了所有执行语句、提交或回滚事务和获取映射器实例的方法。
<T> T selectOne(String statement, Object parameter);
<E> List<E> selectList(String statement, Object parameter);
int insert(String statement, Object parameter);
int update(String statement, Object parameter);
int delete(String statement, Object parameter);
void commit()
void rollback()
17.SqlSessionFactoryBuilder相关API(面试会问)
SqlSession工厂构造器SqlSessionFactoryBuilder
常用API: SqlSessionFactory build(InputStream inputStream)
通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象.
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
18.SqlSessionFactory相关API(面试会问)
SqlSessionFactory 有多个方法可以创建SqlSession实例。
方法 | 解释 |
openSession() | 会默认开启一个事务,但事务不会自动提交。需要手动提交改事务。 |
openSession(boolean autoCommit) | 参数为是否自动提交,设置为true,就不再需要手动提交事务。 |
SqlSession sqlSession = build.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
19.事务机制和实际操作(面试会问)
事务管理
①使用JDBC的事务管理机制
利用java.sql.Connection对象完成对事务的提交commit()、回滚rollback()和关闭close()等操作。
②使用managed的事务管理机制
对于这种机制,mybatis自身不会去实现事务管理,而是让容器如WebLogic、JBOSS等来实现事务的管理。
@Service
public class StudentService{
@Autowired
StudentDao studentDao;
@Transactional(isolation = Isolation.READ_COMMITTED, readOnly = false, rollbackFor = Exception.class)
public int insert(Student record,String hobbys){
return studentDao.insertStudent(record,hobbys);
};
}