Mybatis连接池及动态SQL

本文详细介绍了MyBatis的连接池技术,包括连接池的分类、数据源配置、DataSource的存取和连接获取过程。同时,文章探讨了MyBatis的事务控制,重点讲解了动态SQL语句,如where和foreach标签的使用,以及如何进行多表查询,如一对一、一对多和多对多的查询策略。此外,还提到了sql片段的定义和引用,以提高代码复用性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MyBatis连接池技术

  • 在SqlMapConfig.xml配置文件中,通过<dataSource type="pooled">实现连接池配置

连接池的分类

  • unpolled 不使用连接池的数据源
  • polled 使用连接池的数据源
  • jndi 使用jndi实现的数据源

数据源的配置

<!-- 配置数据源(连接池)信息 --> 
<dataSource type="POOLED">  
	<property name="driver" value="${jdbc.driver}"/>  
	<property name="url" value="${jdbc.url}"/>  
	<property name="username" value="${jdbc.username}"/>  
	<property name="password" value="${jdbc.password}"/> 
</dataSource> 
  • MyBatis 在初始化时,根据的 type 属性来创建相应类型的的数据源 DataSource,即: type=”POOLED”:MyBatis 会创建 PooledDataSource 实例 type=”UNPOOLED” : MyBatis 会创建 UnpooledDataSource 实例 type=”JNDI”:MyBatis 会从 JNDI 服务上查找 DataSource 实例,然后返回使用。

DataSource的存取

  • MyBatis定义了抽象的工厂接口:DataSourceFactory,通过接口的getDataSource()返回数据源DataSource

连接的获取过程

  • 当我们需要创建 SqlSession 对象并需要执行 SQL 语句时,这时候 MyBatis 才会去调用 dataSource 对象 来创建java.sql.Connection对象。也就是说,java.sql.Connection对象的创建一直延迟到执行SQL语句 的时候。

MyBatis的事务控制

MyBatis的事务提交方式

  • 实际是调用JDBC的setAutoCommit()来实现事务控制,这是因为在连接池取出的连接,都会将调用connection.setAutoCommit(false),因此必须使用sqlsession.commit()方法。
//4.创建 SqlSession 对象 
 session = factory.openSession(true); 

此时事务设置成自动提交

动态Sql语句

标签

  • 根据实体类的不同取值,使用不同的sql语句来查询
  • 需求: 如果ID不为空时根据ID查询,如果username不为空时还需要加入用户名作为条件

持久层Dao接口

/** 
 * 根据用户信息,查询用户列表  
 * * @param user 
 *  * @return  */
  List<User> findByUser(User user); 

持久层Dao映射配置

<select id="findByUser" resultType="user" parameterType="user"> 
 select * from user **where 1=1** 
 <if test="username!=null and username != '' ">   
   and username like #{username} 
  </if> 
 <if test="address != null">   
 and address like #{address}  
 </if>    </select> 

注意:标签的 test 属性中写的是对象的属性名,如果是包装类的对象要使用 OGNL 表达式的写法。

测试

@Test  public void testFindByUser()  { 
   User u = new User();   
   u.setUsername("%王%");  
    u.setAddress("%顺义%");   
    //6.执行操作   
    List<User> users = userDao.findByUser(u);   
    for(User user : users) {    
    System.out.println(user);   }   
     } 

where标签

  • 为了简化扇面where 1=1的条件拼装,采用where简化开发

持久层Dao映射配置

<!-- 根据用户信息查询 -->  
<select id="findByUser" resultType="user" parameterType="user">   
<include refid="defaultSql"></include>   
<where> 
   <if test="username!=null and username != '' ">    
    and username like #{username}    
    </if> 
   <if test="address != null">     
   and address like #{address}    
   </if>  
    </where>  
    </select> 

foreach标签

  • 需求:传入多个id查询用户信息,用下面两个sql语句实现
SELECT * FROM USERS WHERE username LIKE '%张%' AND (id =10 OR id =89 OR id=16) 
SELECT * FROM USERS WHERE username LIKE '%张%' AND id IN (10,89,16) 
  • 这样在范围查询时,就要将一个集合中的值,作为参数动态添加进来。

在QueryVo中加入一个List集合用于封装参数

public class QueryVo implements Serializable { 
  private List<Integer> ids; 
 public List<Integer> getIds() {   
 return ids;
   } 
  public void setIds(List<Integer> ids) {  
   this.ids = ids;  
   }     
   }

持久层Dao接口

/** 
  * 根据 id 集合查询用户  
  *  * @param vo  
  *  * @return  
  /  
  List<User> findInIds(QueryVo vo); 

持久层Dao映射配置

<!-- 查询所有用户在 id 的集合之中 -->  
<select id="findInIds" resultType="user" **parameterType="queryvo"**>  
<!--  select * from user where id in (1,2,3,4,5); --> 
  <include refid="defaultSql"></include>   
  <where> 
   <if test="ids != null and ids.size() > 0">     
   <foreach collection="ids" open="id in ( " close=")" item="uid"  separator=",">      
   #{uid}     
   </foreach>   
    </if>  
     </where>  
     </select> 
     sql语句:selet 字段 from user where id in (?)
  • collection:代表要遍历的集合元素,注意编写时不要写#{}
  • open:代表语句的开始部分
  • close:代表结束部分
  • item:代表遍历集合的每个元素,生成的变量名
  • sperator:代表分隔符

测试方法

@Test  public void testFindInIds()  {    
QueryVo vo = new QueryVo();   
List<Integer> ids = new ArrayList<Integer>();   
  ids.add(41);  
  ids.add(42);  
  ids.add(43);  
  ids.add(46);  
  ids.add(57);   
  vo.setIds(ids); 
  //6.执行操作
     List<User> users = userDao.findInIds(vo);   
     for(User user : users) {  
       System.out.println(user);   
       }  
     } 

sql片段

  • 将重复的sql语句提取出来,使用时用Include引用,达到复用sql的目的

定义代码片段

<!-- 抽取重复的语句代码片段 -->  
<sql id="defaultSql">   
select * from user  
</sql>

引用代码片段

<!-- 配置查询所有操作 --> 
<select id="findAll" resultType="user">  
<include refid="defaultSql"></include> 
</select>   
<!-- 根据 id 查询 -->
 <select id="findById" resultType="UsEr" parameterType="int"> 
 <include refid="defaultSql"></include>   
 where id = #{uid} 
 </select> 
 

多表查询:一对多

  • 用户为User表,账户为Account表。一个用户可以有多个账户。

一对一(多对一)查询

  • 需求:查询所有账户信息,关联查询下单用户信息
  • 因为一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询。如果从用户信息出发查询用户名下的账户信息则为一对多查询。

方式1

定义账户信息的实体类

public class Account implements Serializable { 
 
 private Integer id;  
 private Integer uid; 
 private Double money; 
 public Integer getId() { 
   return id;  } 
 public void setId(Integer id) {  
  this.id = id;  }  
 public Integer getUid() {
    return uid;  } 
 public void setUid(Integer uid) {
    this.uid = uid;  }  
public Double getMoney() {   
	return money;  }
public void setMoney(Double money) {
   this.money = money;  } 
    @Override 
 public String toString() {   return "Account [id=" + id + ", uid=" + uid + ", money=" + money + "]";  
  } 
 } 

编写sql语句

SELECT    account.*,   user.username,   user.address FROM   account,   user  WHERE account.uid = user.id 

定义AccountUser类

为了封装上面sql语句的查询结果,定义AccountCustomer类中要包含账户信息同时还要包含用户信息,因此定义AccountUser类继承User类。

public class AccountUser extends Account implements Serializable {    
private String username;  
private String address;  
public String getUsername() {  
 return username;  } 
 public void setUsername(String username) { 
   this.username = username;  }  
 public String getAddress() {   
   return address; 
    } 
 public void setAddress(String address) {  
  this.address = address;  }  
  @Override  public String toString() { 
    return super.toString() + "   AccountUser [username=" + username + ", address=" + address + "]"; 
     		} 
    } 

定义账户的持久层Dao接口

public interface IAccountDao {    /** 
  * 查询所有账户,同时获取账户的所属用户名称以及它的地址信息   * @return   */ 
   List<AccountUser> findAll(); 
  }

定义AccountDao.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.itheima.dao.IAccountDao">  
<!-- 配置查询所有操作-->  
<select id="findAll" resultType="accountuser">   
select a.*,u.username,u.address from account a,user u where a.uid =u.id;  
</select>  
</mapper>

创建AccountTest测试类

public class AccountTest {    
private InputStream in ;  
private SqlSessionFactory factory; 
 private SqlSession session;  
 private IAccountDao accountDao;    
 @Test  public void testFindAll()  {    
 //6.执行操作   
 List<AccountUser> accountusers = accountDao.findAll();   
 for(AccountUser au : accountusers) { 
    System.out.println(au);   
       }      
    }   
  @Before//在测试方法执行之前执行  
 public void init()throws Exception {  
  //1.读取配置文件   
 in = Resources.getResourceAsStream("SqlMapConfig.xml");   
 //2.创建构建者对象   
 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();  
  //3.创建 SqlSession 工厂对象   
  factory = builder.build(in); 
  //4.创建 SqlSession 对象   
  session = factory.openSession(); 
  //5.创建 Dao 的代理对象  
   accountDao = session.getMapper(IAccountDao.class);  
   }   
 @After//在测试方法执行完成之后执行 
  public void destroy() throws Exception{  
   session.commit(); 
  //7.释放资源  
   session.close();  
    in.close();  } } 

此种方法定义了专门的po类作为输出类型,其中定义了sql查询结果集所有的字段。此方法较为简单,企业中使用普遍。

方法二

修改Account类

在Account类中加入User类的对象作为Account类的一个属性

private User user;    
public User getUser() {  
 return user;  
 } 
public void setUser(User user) {   
 this.user = user; 
 } 

修改AccountDao接口中的方法

public interface IAccountDao {    
/**  * 查询所有账户,同时获取账户的所属用户名称以及它的地址信息   * @return   */ 
 List<**Account**> findAll(); } 

返回值改为Account类型,因为Account类中包含了一个User类的对象,可以封装账户所对应的用户信息

重新定义AccountDao.xml文件

<!-- 建立对应关系 --> 
 <resultMap type="account" id="accountMap">  
  <id column="aid" property="id"/>   
  <result column="uid" property="uid"/>   
  <result column="money" property="money"/>   
<!-- 它是用于指定从表方的引用实体属性的 -->  
 <association property="user" javaType="user">   
 <id column="id" property="id"/>    
 <result column="username" property="username"/>    
 <result column="sex" property="sex"/>   
  <result column="birthday" property="birthday"/>    
  <result column="address" property="address"/>   
  </association>  
  </resultMap>   
 <select id="findAll" resultMap="accountMap">   
 select u.*,a.id as aid,a.uid,a.money from account a,user u where a.uid =u.id; 
  </select>

编写测试方法

@Test  public void testFindAll()  {   
 List<Account> accounts = accountDao.findAll();  
  for(Account au : accounts) {    
  System.out.println(au);    
  System.out.println(au.getUser());  
   }
       }

一对多查询

需求:查询所有用户信息及用户关联的账户信息
分析:用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,因此左外连接查询比较合适。

编写Sql语句

SELECT  u.*, acc.id id,  acc.uid,  acc.money FROM  user u LEFT JOIN account acc ON u.id = acc.uid

User类中加入List

public class User implements Serializable { 
 
 private Integer id; 
 private String username; 
 private Date birthday;
 private String sex;  
 private String address;  
 private List<Account> accounts;     
 public List<Account> getAccounts() {   
 return accounts;  }  
 public void setAccounts(List<Account> accounts) {   
 this.accounts = accounts;  } 
  public Integer getId() {  
   return id;  } 
  public void setId(Integer id) {  
   this.id = id;  }  
  public String getUsername() {   
  return username;  }  
  public void setUsername(String username) {  
   this.username = username;  }  
   public Date getBirthday() {   
   return birthday;  } 
 public void setBirthday(Date birthday) {   
 this.birthday = birthday;  }  
 public String getSex() {  
 return sex;  }  
 public void setSex(String sex) {   
 this.sex = sex;  } 
 public String getAddress() {   
 return address;  } 
 public void setAddress(String address) {  
  this.address = address;  } 
  @Override  public String toString() {   return "User [id=" + id + ", username=" + username + ", birthday=" + birthday + ", sex=" + sex + ", address="     + address + "]";  } } 

用户持久层Dao接口中加入查询方法

List <User> findAll();

用户持久层Dao映射文件配置

<mapper namespace="com.itheima.dao.IUserDao">   
 <resultMap type="user" id="userMap">   
 <id column="id" property="id"></id>   
 <result column="username" property="username"/>  
 <result column="address" property="address"/>   
 <result column="sex" property="sex"/>   
 <result column="birthday" property="birthday"/>  
  <!-- collection 是用于建立一对多中集合属性的对应关系    ofType 用于指定集合元素的数据类型    --> 
  <collection property="accounts" ofType="account">   
  <id column="aid" property="id"/>    
  <result column="uid" property="uid"/>   
   <result column="money" property="money"/> 
   </collection>  
   </resultMap> 
  <!-- 配置查询所有操作 --> 
   <select id="findAll" resultMap="userMap">  
    select u.*,a.id as aid ,a.uid,a.money from user u left outer join account a on u.id =a.uid  
    </select> 
    </mapper> 

collection 部分定义了用户关联的账户信息。
表示关联查询结果集 property=“accList” :
关联查询的结果集存储在 User 对象的上哪个属性。 ofType=“account” :
指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名。

测试方法

public class UserTest {
private InputStream in ;
private SqlSessionFactory factory;
private SqlSession session;
private IUserDao userDao;
@Test public void testFindAll() {
//6.执行操作
List users = userDao.findAll();
for(User user : users) {
System.out.println("-------每个用户的内容---------");
System.out.println(user);
System.out.println(user.getAccounts());
}
}
@Before//在测试方法执行之前执行
public void init()throws Exception {
//1.读取配置文件
in = Resources.getResourceAsStream(“SqlMapConfig.xml”);
//2.创建构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.创建 SqlSession 工厂对象
factory = builder.build(in);
//4.创建 SqlSession 对象
session = factory.openSession();
//5.创建 Dao 的代理对象
userDao = session.getMapper(IUserDao.class); }
@After//在测试方法执行完成之后执行
public void destroy() throws Exception{
session.commit();
//7.释放资源
session.close();
in.close(); } }

多表查询(多对多)

Role到User多对多

需求: 实现查询所有对象并加载它所分配的用户信息
分析:查询角色需要使用Role表,但角色分配的用户信息需要使用中间表USER_ROLE才能关联到用户信息

编写角色实体类

public class Role implements Serializable { 
 
    private Integer roleId;     
    private String roleName; 
    private String roleDesc; 
 
    //多对多的关系映射:一个角色可以赋予多个用户   
    private List<User> users; 
 
    public List<User> getUsers() {         return users;     } 
 
    public void setUsers(List<User> users) {         this.users = users;     } 
 
    public Integer getRoleId() {         return roleId;     } 
 
    public void setRoleId(Integer roleId) {         this.roleId = roleId;     } 
 
    public String getRoleName() {         return roleName;     } 
 
    public void setRoleName(String roleName) {         this.roleName = roleName;     } 
 
    public String getRoleDesc() {         return roleDesc;     } 
 
    public void setRoleDesc(String roleDesc) {         this.roleDesc = roleDesc;     } 
 
    @Override     public String toString() {         return "Role{" +                 "roleId=" + roleId +                 ", roleName='" + roleName + '\'' +                 ", roleDesc='" + roleDesc + '\'' +                 '}';
    	}
    }

编写Role持久层接口

List<Role> findAll();

编写映射文件

<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 column="id" property="id"></id>             
     <result column="username" property="username"></result>             
     <result column="address" property="address"></result>             
     <result column="sex" property="sex"></result>             
     <result column="birthday" property="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>

编写测试类

 @Test     public void testFindAll(){        
  List<Role> roles = roleDao.findAll(); 
    for(Role role : roles){             
    System.out.println("---每个角色的信息----");             
    System.out.println(role);             
    System.out.println(role.getUsers());         
    }    
     } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值