Mybatis连接池和事务
- 在配置处指定是否使用连接池
MyBatis 在初始化时,根据<dataSource>的 type 属性来创建相应类型的的数据源 DataSource,即:
type=”POOLED”:MyBatis 会创建 PooledDataSource 实例
type=”UNPOOLED” : MyBatis 会创建 UnpooledDataSource 实例
type=”JNDI”:MyBatis 会从 JNDI 服务上查找 DataSource 实例,然后返回使用
<!-- 配置数据源(连接池)信息 -->
<dataSource type="POOLED">
...
</dataSource>
- Mybatis 中事务提交方式
在 JDBC 中我们可以通过手动方式将事务的提交改为手动方式,通过 setAutoCommit()方法,在mybatis中也可以设置事务自动提交。
//4.创建 SqlSession 对象
session = factory.openSession(true); //传入参数则可以设置事务自动提交
之前的 CUD 操作过程中,我们都要手动进行事务的提交,原因是 setAutoCommit()方法,在执行时它的值被设置为 false 了,所以我们在 CUD 操作中,必须通过 sqlSession.commit()方法来执行提交操作。
设置为自动提交方式为 false 再根据情况决定是否进行提交,这种方式更常用。因为我们可以根据业务情况来决定提交是否进行提交。
@After //用于在测试方法之后执行
public void destroy() throws Exception {
//提交事务
sqlSession.commit(); //执行增删改时会自动提交
//6 释放资源
sqlSession.close();
...
}
Mybatis 的动态 SQL 语句
<if>标签
根据实体类的不同取值,使用不同的 SQL 语句来进行查询。比如在 id 如果不为空时可以根据 id 查询,如果 username 不空时还要加入用户名作为条件。
这种情况在我们的多条件组合查询 中经常会碰到。
//注意:<if>标签的 test 属性中写的是对象的属性名,如果是包装类的对象要使用 OGNL 表达式的写法。
另外要注意 where 1=1 的作用 --- 拼接字符串
<!--根据条件查询-->
<select id="findUserByCondition" parameterType="User" resultType="User">
select * from user where 1=1
<if test="username != null">
and username = #{username}
</if>
<if test="sex!=null">
and sex = #{sex}
</if>
</select>
//Preparing: select * from user where 1=1 and username = ? and sex = ?
<where>标签
//就是省略了在每句sql后面加 where 1=1
<select id="findUserByCondition" parameterType="User" resultType="com.itheima.domain.User">
select * from user
<where>
<if test="username != null">...</if>
<if test="sex!=null">...</if>
</where>
</select>
<foreach>标签
传入多个 id 查询用户信息,sql实现:
SELECT * FROM USERS WHERE id IN (10,89,16)
这样我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。
//在 QueryVo 中加入一个 List 集合用于封装参数
public class QueryVo {
private User user;
private List<Integer> ids;
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
...
}
//dao配置
<!--根据QueryVo中的Id集合实现查询用户列表-->
<select id="findUserInIds" resultType="user" parameterType="queryvo">
select * from user
<where>
<if test="ids != null and ids.size()>0">
<foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>
//测试foreach标签的使用
@Test
public void testFindInIds() throws Exception {
QueryVo vo = new QueryVo();
List<Integer> list = new ArrayList<Integer>();
list.add(41);
list.add(42);
list.add(43);
vo.setIds(list);
//5 执行查询所有
List<User> users = dao.findUserInIds(vo);
for (User user : users) {
System.out.println(user);
}
}
了解
<!--了解的内容,抽取重复的sql语句-->
<sql id="defaultUser">
select * from user
</sql>
//使用include引入
<select id="findAll" resultType="user">
<include refid="defaultUser"></include>
<!--select * from user;-->
</select>
Mybatis 多表查询
- 示例:用户和账户
一个用户可以有多个账户
一个账户只能属于一个用户(多个账户也可以属于同一个用户) - 步骤:
1 建立两张表,用户表、账户表
让用户表和账户表之间具备一对多的关系:需要使用外键在账户表中添加
2 建立连个实体类:用户实体类和账户实体类
让用户和账户实体类能体现出一对多的关系
3 建立两个配置文件
用户配置文件和账户配置文件
4 实现配置
当我们查询用户时,可以同时得到用户下所包含的账户信息
当我们查询账户时,可以同时得到账户所属的用户信息
多对一
//account实体类
//从表实体应该包含一个主表实体的对象引用
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
//测试类
//测试查询账户同时获取用户信息
@Test
public void testFindAll() throws Exception {
//5 执行查询所有
List<Account> accounts = dao.findAll();
for (Account account : accounts) {
System.out.println("--------------------------");
System.out.println(account);
System.out.println(account.getUser());
}
}
<!--查询所有用户-->
<select id="findAll" resultMap="accountUserMap">
select u.*,a.id as aid,a.uid,a.money from user u , account a where u.id = a.uid;
</select>
一对多
//user实体类添加字段
//一对多关系映射,主表实体应该包含从表实体的集合引用
private List<Account> accounts;
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
<!--定义User的resultMap-->
<resultMap id="userAccountMap" type="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
<!--配置user对象中account集合的映射-->
<collection property="accounts" ofType="account">
<!--property对应实体类属性,column是数据库中字段,可以起别名-->
<id column="aid" property="id"></id>
<result column="uid" property="uid"></result>
<result column="money" property="money"></result>
</collection>
</resultMap>
<!--查询所有用户-->
<select id="findAll" resultMap="userAccountMap">
select * from user u LEFT OUTER JOIN account a on u.id = a.uid;
</select>
多对多
- 示例:用户和角色
一个用户可以有多个角色,一个角色也可以赋予多个用户 - 步骤:
1 建立两张表,用户表、账户表
让用户表和角色表之间具备多对多的关系:需要使用中间表,中间表中包含各自的主键。
2 建立连个实体类:用户实体类和角色实体类
让用户和角色实体类能体现出多对多的关系
3 建立两个配置文件
用户配置文件和角色配置文件
4 实现配置
当我们查询用户时,可以同时得到用户下的角色信息
当我们查询角色时,可以同时得到角色所赋予的用户信息
查询角色包含用户信息
//类似之前配置用户和账户的一对多关系
//多对多的关系映射,一个角色可以赋予多个用户
private List<User> users;
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
//IRoleDao.xml
<mapper namespace="com.itheima.dao.IRoleDao">
<!--定义role表的resultMap-->
<resultMap id="roleMap" type="role">
<id property="roleId" column="rid"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
<collection property="users" ofType="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
</collection>
</resultMap>
<!--查询所有-->
<select id="findAll" resultMap="roleMap">
SELECT u.*,r.id as rid,r.ROLE_NAME,r.ROLE_DESC from role r
left outer join user_role ur on r.id = ur.rid
left outer join user u on u.id = ur.uid
</select>
</mapper>
查询用户得到包含用户角色信息
//与之前的配置类似,在user实体类添加roles集合,更改配置文件映射和sql语句
<!--定义User的resultMap-->
<resultMap id="userMap" type="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
<!--配置角色集合的映射-->
<collection property="roles" ofType="role">
<id property="roleId" column="rid"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
</collection>
</resultMap>
<!--查询所有用户-->
<select id="findAll" resultMap="userMap">
SELECT u.*,r.id as rid,r.ROLE_NAME,r.ROLE_DESC from user u
left outer join user_role ur on u.id = ur.uid
left outer join role r on r.id = ur.rid
</select>
JNDI
- 新建一个maven的webapp项目,导入所需要的jar的坐标
- 在webapp下新建一个目录META-INF,建立context.xml用于配置数据库连接的信息
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!--
<Resource
name="jdbc/eesy_mybatis" 数据源的名称
type="javax.sql.DataSource" 数据源类型
auth="Container" 数据源提供者
maxActive="20" 最大活动数
maxWait="10000" 最大等待时间
maxIdle="5" 最大空闲数
username="root" 用户名
password="root" 密码
driverClassName="com.mysql.jdbc.Driver" 驱动类
url="jdbc:mysql://localhost:3306/eesy_mybatis" 连接url字符串
/>
-->
<Resource
name="jdbc/eesy_mybatis"
type="javax.sql.DataSource"
auth="Container"
maxActive="20"
maxWait="10000"
maxIdle="5"
username="root"
password="root"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/eesy_mybatis"
/>
</Context>
- 在SqlMapConfig.xml中配置mybatis环境,数据库引用文件
<environments default="mysql">
<!-- 配置mysql的环境 -->
<environment id="mysql">
<!-- 配置事务控制的方式 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置连接数据库的必备信息 type属性表示是否使用数据源(连接池)-->
<dataSource type="JNDI">
<property name="data_source" value="java:comp/env/jdbc/eesy_mybatis"/>
</dataSource>
</environment>
</environments>
- 将项目部署在tomcat上
//index.jsp
<%@page language="java" contentType="text/html; charset=utf-8" pageEncoding="UTF-8"%>
<html>
<body>
<h2>Hello World!</h2>
<%--jsp中的代码经过服务器,如果使用单元测试,则tomcat中的配置未被使用,会报错--%>
<%
//1 读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2 创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in); //SqlSessionFactory是一个接口,不能直接new
//3 使用工厂生产一个SqlSession对象
SqlSession sqlSession = factory.openSession();
//4 使用SqlSession创建dao接口的代理对象(代理模式 --- 增强方法)
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
//5 使用代理对象执行方法
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
//6 释放资源
sqlSession.close();
in.close();
%>
</body>
</html>
当项目部署完毕,启动服务器访问路径,就会执行jsp中代码,调用findAll方法在控制台打印数据库中user信息。