Day_03
连接池、动态SQL;
连接池
(1)连接池:
在实际开发中都会使用连接池,因为它可以减少我们获取连接所消耗的时间;
(2)mybatis中的连接池:
mybatis连接池提供了3种配置方式;
配置的位置:主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式。
type属性的值,有3种:
- POOLED:采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
- UNPOOLED:采用传统的获取连接的方式,虽然也实现javax.sql.DataSource接口,但是并没有使用池的思想
- JNDI:采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样的
注意:如果不是web或者maven的war工程,是不能使用的;学习时使用的是tomcat服务器,采用的连接池就是dbcp连接池;
Mybatis事务
什么是事务?
- 事务的四大特性ACID;
- 不考虑隔离性会产生的3个问题;
- 解决办法:四种隔离级别;
它是通过sqlsession对象的commit方法和rollback方法实现事务的提交和回滚,默认update和delete是不自动提交的,需要手动commit;
动态SQL
(1) if 标签
<!-- 根据条件判断user的username是否为空,来动态的增加SQL条件语句 -->
<select id="findByCondition" resultType="com.ming.domain.User" parameterType="com.ming.domain.User">
<!-- 这里有个where1=1
select * from user where 1=1
<if test="username!=null">
and username=#{username}
</if>
<if test="sex!=null">
and sex=#{sex}
</if>
-->
<!-- 写到<where>里面 不需要 where1=1 -->
select * from user
<where>
<if test="username!=null">
and username=#{username}
</if>
<if test="sex!=null">
and sex=#{sex}
</if>
</where>
</select>
测试:
@Test
public void findByCondition() {
User u = new User();
User u0 = new User();
User u1 = new User();
//username为空,会查询所有
List<User> users0 = userDao.findByCondition(u0);
users0.stream().forEach(System.out::println);
//username不为空,会查询username=#{username}
u.setUsername("老王");
List<User> users = userDao.findByCondition(u);
users.stream().forEach(System.out::println);
//username和sex不为空,会查询username=#{username} sex=#{sex}
u1.setUsername("老王");
u1.setSex("女");
List<User> users1 = userDao.findByCondition(u1);
users1.stream().forEach(System.out::println);
session.commit();
}
(2) where和foreach标签
用于多个查询的sql:select * from user where id in(41, 42, 45);
通过一个类中传入集合的方法;
定义类:
/**
* @author A
*/
public class QueryVo {
//为测试OGNL #{user.username}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
private User user;
//为测试动态SQL if标签
private List<Integer> ids;
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
}
定义接口方法
/**
* 根据QueryVo 中提供的id集合,查询用户信息 where id in (...)
* @param vo
* @return
*/
List<User> findByIdGroup(QueryVo vo);
定义mapper.xml的SQL
<!-- 条件查询的sql select * from user where id in(41,42,46) 条件:满足id在(...)中 通过一个 QueryVo类 传入集合的方法 -->
<select id="findByIdGroup" resultType="com.ming.domain.User" parameterType="com.ming.domain.QueryVo">
select * from user
<!--<include refid="selectFrUser"></include>-->
<where>
<if test="ids != null and ids.size()>0">
<!-- 这里有5个标签 collections open close item separator 注意 item要和下面的#{user.id}相对应 -->
<foreach collection="ids" open="and id in (" close=")" item="user.id" separator=",">
#{user.id}
</foreach>
</if>
</where>
</select>
测试:
@Test
public void findByIds(){
QueryVo queryVo = new QueryVo();
List<Integer> list = new ArrayList<Integer>();
list.add(41);
list.add(42);
list.add(46);
queryVo.setIds(list);
List<User> users = userDao.findByIdGroup(queryVo);
users.stream().forEach(System.out::println);
//session.commit();
}
(3) 复用SQL语句
对于经常重复使用的SQL语句,如SELECT * FROM user,很多<select>...里面都会用到,这句话经常用,可以作为一个<sql>标签,作为引用导入,有点像JSP/HTML;
<sql id="selectFrUser">
select * from user
</sql>
<!-- 条件查询的sql select * from user where id in(41,42,46) 条件:满足id在(...)中 通过一个 QueryVo类 传入集合的方法 -->
<select id="findByIdGroup" resultType="com.ming.domain.User" parameterType="com.ming.domain.QueryVo">
<!--下面这句话经常用 可以作为一个<sql>标签 作为引用导入 有点像JSP/HTML-->
-- select * from user
<include refid="selectFrUser"></include>
<where>
<if test="ids != null and ids.size()>0">
<!-- 这里有5个标签 collections open close item separator 注意 item要和下面的#{user.id}相对应 -->
<foreach collection="ids" open="and id in (" close=")" item="user.id" separator=",">
#{user.id}
</foreach>
</if>
</where>
</select>
注意: 尽量不要使用分号,sql语句可能会拼接
(以上内容的测试项目为 mybatis_01)
Day_04
多表查询(一对一、一对多、多对多)
为防止对之前代码的影响(除了测试Account还增加了role角色表),新建了UserCopy类以及对应的xml、user_copy表;
示例:用户和账户
- 一个用户可以有多个账户
- 一个账户只能属于一个用户(多个账户也可以属于同一个用户)
步骤:
1、建立两张表:用户表 帐户表
让用户表和账户表之前具备一对多的关系:需要使用外键在帐户表上添加
2、建立两个实体类:用户实体类和账户实体类,让用户和账户的实体类能体现出来一对多的关系
3、配置两个配置文件
用户的配置文件
账户的配置文件
4、实现配置:
当我们查询用户的时候,可以同时得到用户下所包含的信息
当我们查询账户时,我们可以同时得到账户所属的用户信息
1. 一对一/一对多 映射
一张用户表,一张账户表;
一个用户下可能有多个账户,一个账户只对应一个用户;
表结构:
uid表示用户的id;
(1) 从账户表查询,返回每个账户信息+每个账户对应的用户信息,即一对一查询;
Account表的实体类应该包含一个主表实体的对象引用user;
/**
* @author A
*/
public class Account implements Serializable {
//多封装一个User属性,查询的时候 从Acount关联对应的User信息
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
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 +
'}';
}
}
Account的DAO接口IAccountDao
public interface IAccountDao {
/**
* 查询所有账户
* @return
*/
List<Account> findAll();
/**
* 查询所有账户+对应的用户的username和地址
* @return
*/
List<AccountUser> findAcountUser();
/**
* 查询所有账户+对应的用户的username和地址,封装了User对象
* @return
*/
List<Account> findAcountUser1();
}
IAccountDao的Mapper .xml
注意:
(1) 一对一的映射,包含一个user对象,ResultMap里面加一栏<association>...
(2) <association>栏里面的javaType一定要指名user的实体类名,column中指名从表的外键;
<!--一对一的关系映射,配置封装user的内容 column中指名从表的外键 property="user"指的是单个实体类的引用-->
<association property="user" column="uid" javaType="com.ming.domain.User">
测试:
@Test
public void testSelectAll2() {
List<Account> accounts = accountDao.findAcountUser1();
accounts.stream().forEach((a) -> {
System.out.println(a);
System.out.println(a.getUser());
});
}
(2) 从user_copy表去查询所有的user,包含每个user的信息+每个user的账户(可以是多个,所以定义成一个List<Account>),即一对多的映射;
定义user表的实体类,主表实体中应该包含从表实体的集合引用List<Account>
/**
* @author A
* User表的实体类;
*/
public class User implements Serializable {
//一个user有多个ACCOUNT,建立 一对多的关系, 多封装一个List<Account>属性,查询的时候 从User关联对应的多个Acount信息
private List<Account> accounts;
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
...
User的DAO接口
区别于之前的<association>栏,这里使用<collection>标签,对应的List<Account>集合;
<!--一对多的关系映射,配置封装List<Account>的内容(一对多会自动封装到List<Account>里面) column中指名从表的外键(这里User表没有外键) property="accounts"指的是单个实体类的引用 javaType指account的类型(不是accounts的类型List)-->
<collection property="accounts" ofType="com.ming.domain.Account">
<id property="id" column="aid"/><!-- 这里property指实体类Account的属性名 column因为id与User里面的id重复,所以改名aid -->
测试
@Test
public void testSelectAll2() {
List<User> users = userDao.findAcountUser1();
users.stream().forEach((u) -> {
System.out.println(u);
System.out.println(u.getAccounts());
System.out.println();
});
}
2. 多对多映射
一个用户有多个身份;
一个身份个能多个用户都拥有;
步骤:
1、建立两张表:用户表 用户表
让用户和角色表具有多对多的关系。需要使用中间表,中间表中包含各自的主键,在中间表中是外键。
2、建立两个实体类:用户实体类和角色实体类
让用户和角色的实体类能体现出来多对多的关系
各自包含对方一个集合引用
3、配置两个配置文件
用户的配置文件
角色的配置文件
4、实现配置:
当我们查询用户的时候,可以同时得到用户下所包含角色的信息
当我们查询角色时,我们可以同时得到角色所赋予的用户信息
表结构:
user_copy与之前的user表一样;
角色表role
用户和角色的关联表user_role
(1) 从role查user,多对多
定义role表的实体类,一个实体表中包含另一个实体表的集合引用List<User>
public class Role implements Serializable {
private Integer roleId;
private String roleName;
private String roleDesc;
/**
* 多对多的关系映射:一个角色可以赋予多个用户
*/
private List<UserCopy> users;
public List<UserCopy> getUsers() {
return users;
}
public void setUsers(List<UserCopy> users) {
this.users = users;
}
...
role的DAO接口
public interface IRoleDao {
/**
* 查询所有角色
* @return
*/
List<Role> findAll();
/**
* 多表查询 经过中间表 查询所有角色
* @return
*/
List<Role> findAll1();
IRoleDao的XML配置
与前面一样,因为持有一个UserCopy的集合List<UerCopy>,使用<collection>标签
测试
@Test
public void testSelectAll1() {
List<Role> roles= roleDao.findAll1();
roles.stream().forEach((r)->{
System.out.println(r);
System.out.println(r.getUsers());
System.out.println();
});
}
(2) 从user_copy查role,多对多
与Role;类似,定义user_copy表的实体类,包含另一个实体表的集合引用List<Role>
public class UserCopy implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
/**
* 多对多的关系映射:一个用户可以对应多个角色
*/
private List<Role> roles;
Dao接口
/**
* @author A
* User的持久层接口
*/
public interface IUserCopyDao {
/**
* 查询所有UserCopy表的记录-操作
* @return
*/
List<UserCopy> findAll();
/**
* 关联查询 查询所有UserCopy表的记录-操作
* @return
*/
List<UserCopy> findAll1();
}
IUserCopyDao的XML配置
测试
@Test
public void testSelectAll1() {
List<UserCopy> users= userCopyDao.findAll1();
users.stream().forEach((r)->{
System.out.println(r);
System.out.println(r.getRolse());
System.out.println();
});
}
3. JNDI
JNDI是Java命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一。
4. 补充: MySQL的多表查询
可以在 SELECT, UPDATE 和 DELETE 语句中使用 Mysql 的 JOIN 来联合多表查询。
JOIN 按照功能大致分为如下三类:
- INNER JOIN(内连接,或等值连接):获取两个表中字段匹配关系的记录。
- LEFT JOIN(左连接):获取左表所有记录,即使右表没有对应匹配的记录。
- RIGHT JOIN(右连接): 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录。
(1) 表结构测试
(2) 内连接
接下来我们就使用MySQL的INNER JOIN(也可以省略 INNER 使用 JOIN,效果一样)来连接以上两张表来读取runoob_tbl表中所有runoob_author字段在tcount_tbl表对应的runoob_count字段值;
(3) 左/右连接
- MySQL LEFT JOIN
MySQL left join与join有所不同,会读取左边数据表的全部数据,即便右边表无对应数据。
- MySQL RIGHT JOIN
与左连接相似,MySQL RIGHT JOIN会读取右边数据表的全部数据,即便左边边表无对应数据。
(4) 其他:全连接、左/右独有;
全连接:
两表关联,查询左表独有的数据:
两表关联,查询右表独有的数据: