20-MyBatis(2)
MyBatis 动态 SQL#
简介#
- 动态 SQL 是 MyBatis 强大特性之一。极大的简化我们拼装 SQL 的操作
- 动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似
- MyBatis 采用功能强大的基于 OGNL 的表达式来简化操作
- if
- choose (when,otherwise)
- trim (where,set)
- foreach
- OGNL (Object Graph Navigation Language) 对象图导航语言,这是一种强大的表达式语言,通过它可以非常方便的来操作对象属性。类似于我们的 EL,SpEL 等。
- 访问对象属性:person.name
- 调用方法:person.getName()
- 调用静态属性/方法:@java.lang.Math@PI,@java.util.UUID@randomUUID()
- 调用构造方法:new cn.edu.nuist.Person('admin').name
- 运算符:+,-,*,/,%
- 逻辑运算符:in,not in,>,>=,<,<=,==,!=
- 注意:xml 中特殊符号如 ",>,< 等这些都需要使用转义字符
if & where#
<if>
用于完成简单的判断
<!-- List<Emp> getEmpListByConditions(); -->
<select id="getEmpListByConditions" resultType="Emp">
SELECT eid, ename, age, sex, did FROM emp WHERE 1 = 1
<if test="eid != null">
AND eid = #{eid}
</if>
<if test="ename != null and ename != ''">
AND ename = #{ename}
</if>
<if test="age != null and age != '' ">
AND age = #{age}
</if>
<if test="sex == 1 or sex == 0">
AND sex = #{sex}
</if>
</select>
<where>
用于添加 where 关键字,并解决 SQL 语句的条件中第一个 AND 或者 OR 的问题
<!-- List<Emp> getEmpListByConditions(); -->
<select id="getEmpListByConditions" resultType="Emp">
SELECT eid, ename, age, sex, did FROM emp
<where>
<!-- 添加 where 关键字,去掉多余的 and -->
<if test="eid != null">
AND eid = #{eid}
</if>
<if test="ename != null and ename != ''">
AND ename = #{ename}
</if>
<if test="age != null and age != '' ">
AND age = #{age}
</if>
<if test="sex == 1 or sex == 0">
AND sex = #{sex}
</if>
</where>
</select>
Quiz:若 AND 放在条件尾部,此时如果只给 age 条件,SQL 语句末尾就会多出一个 AND
<!-- List<Emp> getEmpListByConditions(); -->
<select id="getEmpListByConditions" resultType="Emp">
SELECT eid, ename, age, sex, did FROM emp
<where>
<!-- 添加 where 关键字,去掉多余的 and -->
<if test="eid != null">
eid = #{eid} AND
</if>
<if test="ename != null and ename != ''">
ename = #{ename} AND
</if>
<if test="age != null and age != '' ">
age = #{age} AND
</if>
<if test="sex == 1 or sex == 0">
sex = #{sex}
</if>
</where>
</select>
测试时报错:
Preparing: SELECT eid, ename, age, sex, did FROM emp WHERE age = ? AND
Parameters: 40(Integer)
trim#
trim 可以在条件判断完的 SQL 语句前后添加或者去掉指定的字符(可解决上述 where 尾部多一个 AND 的问题)
- prefix:在 SQL 前添加指定的前缀
- prefixOverrides:去掉 SQL 中指定的前缀
- suffix:在 SQL 后添加指定的后缀
- suffixOverrides:去掉 SQL 中指定的后缀
<!-- List<Emp> getEmpListByConditions(); -->
<select id="getEmpListByConditions" resultType="Emp">
SELECT eid, ename, age, sex, did FROM emp
<!-- 添加 where 关键字,去掉多余的 and 或者 or -->
<trim prefix="where" suffixOverrides="and|or">
<if test="eid != null">
eid = #{eid} AND
</if>
<if test="ename != null and ename != ''">
ename = #{ename} AND
</if>
<if test="age != null and age != '' ">
age = #{age} AND
</if>
<if test="sex == 1 or sex == 0">
sex = #{sex}
</if>
</trim>
</select>
choose(when|otherwise)#
choose 主要是用于分支判断,类似于 Java 中的 switch-case,只会满足所有分支中的一个
<!-- void insertEmp(Emp emp); [1|0 → 男|女] -->
<insert id="insertEmp">
INSERT INTO emp(eid, ename, age, sex) VALUES (
null, #{ename}, #{age},
<choose>
<when test="sex == 1">'男'</when>
<when test="sex == 0">'女'</when>
<otherwise>'不详'</otherwise>
</choose>
)
</insert>
set#
set 主要是用于解决修改操作中 SQL 语句中可能多出逗号的问题
<update id="updateEmpByConditionSet">
UPDATE emp
<set>
<if test="ename != null and ename != ''">
ename = #{ename},
</if>
<if test="age != null and age != ''">
age = #{age},
</if>
<if test="sex == 1 or sex == 0">
sex = #{sex}
</if>
</set>
WHERE eid = #{eid}
</update>
foreach#
foreach 主要用于循环迭代。
- collection:要迭代的集合
- item:当前从集合中迭代出的元素
- open:循环体的开始字符
- close:循环体的结束字符
- separator:元素与元素之间的分隔符
- index:迭代的是 List 集合,index 表示的当前元素的下标;迭代的 Map 集合,index 表示的当前元素的 key
- 批量删除员工
- 方式一
<!-- void deleteEmps(String eids); --> <delete id="deleteEmps"> DELETE FROM emp WHERE eid IN (${value}) </delete> =============================================== Preparing: DELETE FROM emp WHERE eid IN (10, 12) Parameters: Updates: 2
- 方式二
<!-- void deleteEmpsByList(List<Integer> eids); --> <delete id="deleteEmpsByList"> DELETE FROM emp WHERE eid IN <foreach collection="list" item="eid" separator="," open="(" close=")"> #{eid} </foreach> </delete> ============================================================= Preparing: DELETE FROM emp WHERE eid IN ( ? , ? ) Parameters: 9(Integer), 6(Integer) Updates: 2
- 方式一
- 批量插入
<!-- void batchInsertEmps(@Param("emps")Emp[] emps); --> <insert id="batchInsertEmps"> INSERT INTO emp(eid, ename, age, sex) VALUES <foreach collection="emps" item="emp" separator=","> (null, #{emp.ename}, #{emp.age}, #{emp.sex}) </foreach> </insert>
- 批量修改
<!-- 把每条数据修改为相同的内容 UPDATE emp SET ... WHERE eid IN (1, 2, 3); UPDATE emp SET ... WHERE eid=1 OR eid=2 OR eid=3; 把每条数据修改为相对应内容 (一次执行多条 SQL 语句要求 jdbc.url 后有参数 allowMultiQueries) UPDATE emp SET ... WHERE eid=1; UPDATE emp SET ... WHERE eid=2; UPDATE emp SET ... WHERE eid=3; --> <!-- void batchUpdateEmps(@Param("emps")Emp[] emps); --> <!-- jdbc.url=jdbc:mysql:///test?allowMultiQueries=true --> <update id="batchUpdateEmps"> <foreach collection="emps" item="emp"> UPDATE emp SET ename=#{emp.ename}, age=#{emp.age} , sex=#{emp.sex} WHERE eid=#{emp.eid}; </foreach> </update>
sql#
sql 标签是用于抽取可重用的 sql 片段,将相同的,使用频繁的 SQL 片段抽取出来,单独定义,方便多次引用。
- 抽取 SQL
<sql id="empColumns">SELECT eid, ename, age, sex FROM emp</sql>
- 引用 SQL
<include refid="empColumns"/>
MyBatis 缓存机制#
缓存机制简介#
- MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率
- MyBatis系统中默认定义了两级缓存:一级缓存、二级缓存
- 默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启
- 二级缓存需要手动开启和配置,它是基于 namespace 级别的缓存
- 为了提高扩展性。MyBatis 定义了缓存接口 Cache。我们可以通过实现 Cache 接口来自定义二级缓存
一级缓存#
一级缓存的使用
- 一级缓存(local cache),即本地缓存,作用域默认为 SqlSession。当 Session flush 或 close 后,该 Session 中的所有 Cache 将被清空
- 本地缓存不能被关闭,但可以调用 clearCache() 来清空本地缓存,或者改变缓存的作用域
- 在 MyBatis3.1 之后,可以配置本地缓存的作用域。在 mybatis.xml 中配置
- 一级缓存的工作机制
- 同一次会话期间只要查询过的数据都会保存在当前 SqlSession 的一个 Map 中
- key:hashCode + 查询的 SqlId + 编写的 sql 查询语句 + 参数
@Test
public void testFirstCache() throws IOException {
SqlSession session = getSqlSessionFactory().openSession(true);
EmpMapper mapper = session.getMapper(EmpMapper.class);
Emp emp1 = mapper.getEmpByEid("3");
Emp emp2 = mapper.getEmpByEid("3");
System.out.println(emp1);
System.out.println("+++++++++++++++++++++");
System.out.println(emp2);
}
======================= 打印控制台 =======================
Preparing: SELECT eid, ename, age, sex FROM emp WHERE eid=?
Parameters: 3(String)
Total: 1
Emp [eid=3, ename=root, age=30, sex=0]
+++++++++++++++++++++
Emp [eid=3, ename=root, age=30, sex=0]
一级缓存失效的几种情况
- 不同的 SqlSession 对应不同的一级缓存
- 同一个 SqlSession 但是查询条件不同
- 同一个 SqlSession 两次查询期间执行了任何一次增删改操作(无论成功与否)
- 同一个 SqlSession 两次查询期间手动清空了缓存
@Test
public void testFirstCache() throws IOException {
SqlSession session = getSqlSessionFactory().openSession(true);
EmpMapper mapper = session.getMapper(EmpMapper.class);
Emp emp1 = mapper.getEmpByEid("13");
System.out.println(emp1);
System.out.println("+++++++++++++++++++++");
mapper.deleteEmpsByString("3"); // 不存在该记录
System.out.println("+++++++++++++++++++++");
Emp emp2 = mapper.getEmpByEid("13");
System.out.println(emp2);
}
======================= 打印控制台 =======================
Preparing: SELECT eid, ename, age, sex FROM emp WHERE eid=?
Parameters: 13(String)
Total: 1
Emp [eid=13, ename=AAA, age=22, sex=0]
+++++++++++++++++++++
Preparing: DELETE FROM emp WHERE eid IN (3)
Parameters:
Updates: 0
+++++++++++++++++++++
Preparing: SELECT eid, ename, age, sex FROM emp WHERE eid=?
Parameters: 13(String)
Total: 1
Emp [eid=13, ename=AAA, age=22, sex=0]
二级缓存#
二级缓存的使用
- 简述
- 二级缓存(second level cache),全局作用域缓存
- 二级缓存默认不开启,需要手动配置
- MyBatis 提供二级缓存的接口以及实现,缓存实现要求 POJO 实现 Serializable 接口
- 一级缓存是 SqlSession 级别的,二级缓存是映射文件级别的。二级缓存在 SqlSession 关闭或提交之后才会生效
- 二级缓存使用的步骤
- 全局配置文件中开启二级缓存
<setting name="cacheEnabled" value="true"/>
- 需要使用二级缓存的映射文件处使用 cache 配置缓存
<cache />
- 注意:POJO 需要实现
Serializable<I>
- 全局配置文件中开启二级缓存
- 二级缓存相关的属性
- eviction:缓存回收策略(默认的是 LRU)
- LRU – 最近最少使用的:移除最长时间不被使用的对象
- FIFO – 先进先出:按对象进入缓存的顺序来移除它们
- SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象
- WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象
- flushInterval:刷新间隔,单位毫秒。默认情况不设置,也就是没有刷新间隔,代表永不刷新,缓存仅在调用语句时刷新
- size:引用数目,正整数。代表缓存最多可以存储多少个对象,太大容易导致内存溢出
- readOnly:只读
- true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势
- false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false
- eviction:缓存回收策略(默认的是 LRU)
- 代码测试1
@Test public void testSecondCache() throws IOException { SqlSession session = getSqlSessionFactory().openSession(true); EmpMapper mapper1 = session.getMapper(EmpMapper.class); Emp emp1 = mapper1.getEmpByEid("13"); System.out.println(emp1); System.out.println("++++++++++++++++++++++++"); EmpMapper mapper2 = session.getMapper(EmpMapper.class); Emp emp2 = mapper2.getEmpByEid("13"); System.out.println(emp2); } ======================= 打印控制台 ======================= Cache Hit Ratio [cn.edu.nuist.mapper.EmpMapper]: 0.0 ← 命中率 Preparing: SELECT eid, ename, age, sex FROM emp WHERE eid=? Parameters: 13(String) Total: 1 Emp [eid=13, ename=AAA, age=22, sex=0] ++++++++++++++++++++++++ Cache Hit Ratio [cn.edu.nuist.mapper.EmpMapper]: 0.0 → 二级缓存未生效 Emp [eid=13, ename=AAA, age=22, sex=0]
- 代码测试2
@Test public void testSecondCache() throws IOException { SqlSession session = getSqlSessionFactory().openSession(true); EmpMapper mapper1 = session.getMapper(EmpMapper.class); Emp emp1 = mapper1.getEmpByEid("13"); System.out.println(emp1); EmpMapper mapper2 = session.getMapper(EmpMapper.class); Emp emp2 = mapper2.getEmpByEid("13"); System.out.println(emp2); // >>> 二级缓存在 SqlSession 关闭或提交之后才会生效 <<< session.commit(); EmpMapper mapper3 = session.getMapper(EmpMapper.class); Emp emp3 = mapper3.getEmpByEid("13"); System.out.println(emp3); session.commit(); EmpMapper mapper4 = session.getMapper(EmpMapper.class); Emp emp4 = mapper4.getEmpByEid("13"); System.out.println(emp4); } ======================= 打印控制台 ======================= Cache Hit Ratio [cn.edu.nuist.mapper.EmpMapper]: 0.0 Preparing: SELECT eid, ename, age, sex FROM emp WHERE eid=? Parameters: 13(String) Total: 1 Emp [eid=13, ename=AAA, age=22, sex=0] Cache Hit Ratio [cn.edu.nuist.mapper.EmpMapper]: 0.0 Emp [eid=13, ename=AAA, age=22, sex=0] put added 0 on heap Cache Hit Ratio [cn.edu.nuist.mapper.EmpMapper]: 0.3333333333333333 [1/3] Emp [eid=13, ename=AAA, age=22, sex=0] Cache Hit Ratio [cn.edu.nuist.mapper.EmpMapper]: 0.5 [2/4] Emp [eid=13, ename=AAA, age=22, sex=0]
缓存的相关配置
- 全局 setting 的 cacheEnable:配置二级缓存的开关,一级缓存一直是打开的
<select>
标签的 useCache 属性:配置这个 select 是否使用二级缓存(一级缓存是一直使用的)- sqlSession.clearCache():只是用来清除一级缓存
- SQL 标签的 flushCache 属性:
- 增删改默认 flushCache=true;查询默认 flushCache=false
- 增删改 SQL 执行以后,会同时清空一级和二级缓存
@Test public void testSecondCache() throws IOException { SqlSession session = getSqlSessionFactory().openSession(true); EmpMapper mapper1 = session.getMapper(EmpMapper.class); Emp emp1 = mapper1.getEmpByEid("13"); System.out.println(emp1); session.commit(); System.out.println("++++++++++++++++"); EmpMapper mapper2 = session.getMapper(EmpMapper.class); Emp emp2 = mapper2.getEmpByEid("13"); System.out.println(emp2); System.out.println("++++++++++++++++"); mapper2.deleteEmpsByString("3"); // 删除操作 System.out.println("++++++++++++++++"); EmpMapper mapper3 = session.getMapper(EmpMapper.class); Emp emp3 = mapper3.getEmpByEid("13"); System.out.println(emp3); } ======================= 打印控制台 ======================= Cache Hit Ratio [cn.edu.nuist.mapper.EmpMapper]: 0.0 Preparing: SELECT eid, ename, age, sex FROM emp WHERE eid=? Parameters: 13(String) Total: 1 Emp [eid=13, ename=AAA, age=22, sex=0] put added 0 on heap ++++++++++++++++ Cache Hit Ratio [cn.edu.nuist.mapper.EmpMapper]: 0.5 Emp [eid=13, ename=AAA, age=22, sex=0] ++++++++++++++++ fault removed 0 from heap fault added 0 on disk Preparing: DELETE FROM emp WHERE eid IN (3) Parameters: Updates: 0 ++++++++++++++++ Cache Hit Ratio [cn.edu.nuist.mapper.EmpMapper]: 0.6666666666666666 Preparing: SELECT eid, ename, age, sex FROM emp WHERE eid=? Parameters: 13(String) Total: 1 Emp [eid=13, ename=AAA, age=22, sex=0]
整合第三方缓存
- 为了提高扩展性。MyBatis 定义了缓存接口 Cache。我们可以通过实现 Cache 接口来自定义二级缓存
- EhCache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点,是 Hibernate 中默认的 CacheProvider
- 整合 EhCache 缓存的步骤
- 导入 ehcache 包,以及整合包,日志包:ehcache-core-2.6.8.jar、mybatis-ehcache-1.0.3.jar、slf4j-api-1.6.1.jar、slf4j-log4j12-1.6.2.jar
- 编写 ehcache.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> <!-- 磁盘保存路径 --> <diskStore path="D:\ehcache" /> <defaultCache maxElementsInMemory="1000" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="true" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache>
- 在映射文件中配置 cache 标签:
<cache type="org.mybatis.caches.ehcache.EhcacheCache" />
PageHelper 分页插件#
PageHelper 是 MyBatis 中非常方便的第三方分页插件。
使用步骤#
- 导入相关包 pagehelper-x.x.x.jar 和 jsqlparser-0.9.5.jar
- 在 MyBatis 全局配置文件中配置分页插件
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin> </plugins>
- 使用 PageHelper 提供的方法进行分页
- 可以使用更强大的 PageInfo 封装返回结果
Page 对象的使用#
在查询之前通过 PageHelper.startPage(pageNum, pageSize)
设置分页信息,该方法返回 Page 对象。
@Test
public void testPageHelper() throws Exception{
SqlSessionFactory ssf = getSqlSessionFactory();
SqlSession session = ssf.openSession();
try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
//设置分页信息
Page<Object> page = PageHelper.startPage(9, 1);
List<Employee> emps = mapper.getAllEmps();
for (Employee employee : emps) {
System.out.println(employee);
}
System.out.println("====== 获取分页相关的信息 ======");
System.out.println("当前页: " + page.getPageNum());
System.out.println("总页码: " + page.getPages());
System.out.println("总条数: " + page.getTotal());
System.out.println("每页显示的条数: " + page.getPageSize());
} finally {
session.close();
}
}
PageInfo 对象的使用#
在查询完数据后,使用 PageInfo 对象封装查询结果,可以获取更详细的分页信息以及可以完成分页逻辑。
PageUtil:
// 首页 上一页 1 2 3 4 5 下一页 尾页
public class PageUtil {
public static String getPageInfo(PageInfo<Emp> pageInfo) {
String path = "/mybatis/";
StringBuilder builder = new StringBuilder();
// 1. 拼接首页
builder.append("<a href='" + path + "emps/1'>首页</a> ");
// 2. 拼接上一页
if(pageInfo.isHasPreviousPage()) {
builder.append("<a href='" + path + "emps/"
+ pageInfo.getPrePage() + "'>上一页</a> ");
} else {
builder.append("上一页 ");
}
// 3. 拼接页码
int[] nums = pageInfo.getNavigatepageNums();
for(int i : nums) {
if(i == pageInfo.getPageNum()) {
builder.append(i+" ");
} else {
builder.append("<a href='" + path
+ "emps/"+i+"'>"+i+"</a> ");
}
}
// 4. 拼接下一页
if(pageInfo.isHasNextPage()) {
builder.append("<a href='" + path + "emps/"
+ pageInfo.getNextPage()+"'></a> ");
} else {
builder.append("下一页 ");
}
// 5. 拼接尾页
builder.append("<a href='" + path + "emps/"
+ pageInfo.getPages() + "'>尾页</a>");
return builder.toString();
}
}
SSM 整合#
整合步骤#
导入 jar 包
- Spring
- SpringMVC
- MyBatis
- 第三方支持:log4j,pageHelper,AspectJ,Jackson,Jstl
搭建 SpringMVC
- web.xml
- DispatcherServlet
- HiddenHttpMethodFilter
- CharacterEncodingFilter
- springMVC.xml
- 扫描控制层组件
- 视图解析器
- DefaultServlet
- MVC Driver
- 可选:MultipartResolver(文件解析器),拦截器
整合 SpringMVC 和 Spring
- web.xml
- ContextLoaderListener
- context-param
- spring.xml
- 扫描组件(排除控制层)
- 事务管理器
搭建 MyBatis
- 核心配置文件
- mapper 接口和 mapper 映射文件
Spring 整合 MyBatis
- spring.xml
- properties 文件的引入
- DataSource 数据源的配置
- 事务管理器
- 开启事务驱动
- SqlSessionFactoryBean(管理 SqlSession)
- MapperScannerConfigurer
- 不同 MyBatis 版本整合 Spring 时使用的适配包:
配置文件#
web.xml
<!-- 配置编码过滤器 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- REST 请求方式处理 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- SpringMVC 核心控制器 DispatcherServlet -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置 Spring 监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 自定义 Spring 配置文件的位置和名称 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
spring.xml
<!-- 扫描组件 -->
<context:component-scan base-package="cn.edu.nuist.ssm">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 引入资源文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 声明事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启事务注解驱动 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 管理 MyBatis 操作数据库的会话对象 SqlSession -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 设置 MyBatis 核心配置文件的路径 -->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 设置数据源 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 设置类型别名 -->
<property name="typeAliasesPackage" value="cn.edu.nuist.ssm.bean"></property>
<!-- 设置映射文件的路径 -->
<property name="mapperLocations" value="classpath:cn/edu/nuist/ssm/mapper/*.xml"></property>
</bean>
<!-- 在所设置的包下,将所有的接口生成动态代理实现类对象,并由 Spring 容器管理 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.edu.nuist.ssm.mapper"></property>
</bean>
springMVC.xml
<!-- 扫描控制层组件 -->
<context:component-scan base-package="cn.edu.nuist.ssm.controller"></context:component-scan>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 默认Servlet -->
<mvc:default-servlet-handler/>
<!-- MVC 驱动 -->
<mvc:annotation-driven />
mybatis-config.xml
<configuration>
<settings>
<!-- 将下划线映射成驼峰, 例如:user_name → userName -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 是否开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
</configuration>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?