mybatis中编写dao实现类的使用方式
简单说一下实现原理:
下面来说一下UserDaoImpl的实现原理
mybatis主配置文件中properties标签的使用
第一种:
<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis1"></property>
<property name="username" value="root"></property>
<property name="password" value="5201314"></property>
第二种:resource属性引用外部配置文件
注意这个配置文件要放到类路径下,也就是src下面
里面存放的就是相应的数据库连接信息
第三种:url属性统一资源定位符,也就是绝对路径
先来看一下这个文件位置
file:///就是一种协议,window系统默认使用这种协议,打开资源都是基于file协议。端口也默认
mybatis主配置文件中typeAlias标签的使用
先来说第一种方式:
然后去修改IUserDao.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">
<!--配置每一个相应dao层的方法,写上相应dao的全类名-->
<mapper namespace="com.pxx.dao.IUserDao">
<!--配置查询结果的列名和实体类的属性名对应的关系-->
<resultMap id="userMap" type="user">
<!-- 主键字段的对应 -->
<id property="userId" column="id"></id>
<!--非主键字段的对应-->
<result property="userName" column="username"></result>
<result property="userAddress" column="address"></result>
<result property="userSex" column="sex"></result>
<result property="userBirthday" column="birthday"></result>
</resultMap>
<!--Id代表方法名称 resultType:代表值类型
这里写上它的全类名 -->
<!--<select id="findAll" resultType="com.pxx.domain.User">-->
<select id="findAll" resultMap="userMap">
select * from user
<!-- select id as userId,username as userName,address as userAddress,
sex as userSex,birthday as userBirthday from user-->
</select>
<!--保存用户信息就不是select语句了
而是insert语句-->
<insert id="saveUser" parameterType="user">
<!--配置插入操作之后,获取插入数据的id-->
<selectKey keyProperty="userId" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into user(username,address,sex,birthday)values(#{userName},#{userAddress},#{userSex},#{userBirthday});
</insert>
<!--这里更新操作就是update-->
<update id="updateUser" parameterType="user">
update user set username=#{userName},address=#{userAddress},sex=#{userSex},birthday=#{userBirthday} where id=#{userId}
</update>
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id=#{id}
</delete>
<!--根据id查询用户的方法,又是查询操作-->
<select id="findById" parameterType="java.lang.Integer" resultMap="userMap">
select * from user where id=#{id}
</select>
<!--根据名称进行模糊查询操作
但是模糊查询中like %名字% 中的这个百分号在在这里面体现不出来
-->
<select id="findByName" parameterType="string" resultMap="userMap">
select * from user where username like #{username}
</select>
<!--查询用户总记录数-->
<select id="findTotal" resultType="int" >
select count(id) from user
</select>
<!--根据queryVo的条件查询用户-->
<select id="findUserByVo" parameterType="com.pxx.domain.QueryVo" resultMap="userMap">
select * from user where username like #{user.userName}
</select>
</mapper>
下面说第二种方式:package
再来多说一点,package这个 标签还可以配置
mybatis数据库连接池
数据库连接池原理
之前再说jdbc连接数据库的时候,我们用到了一个连接池技术Druid数据库连接池技术
DruidDataSourceFactory.createDataSource->这种方法会给我们产生一个数据库连接池
那么具体具体原理就是
用上面这张图来说明一下,首先连接池每一个连接,不可能同时被两个线程所拿到。然后假如线程1拿到2号连接,线程2拿到1号连接。那么连接池里面的连接就会重新编号,3号连接就变成1,然后依次往下面推,线程1,线程2进来的时候就变成7号,8号,也就是队列的对尾
Mybatis中的连接池
配置的位置:
主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式。
type属性的取值:
POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
UNPOOLED 采用传统的获取连接的方式,虽然也实现Javax.sql.DataSource接口,但是并没有使用池的思想。
JNDI 采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样。
注意:如果不是web或者maven的war工程,是不能使用的。
我们课程中使用的是tomcat服务器,采用连接池就是dbcp连接池。
下面来对比一下POOLED连接池与UNPOOLED连接池的实现原理
先配置POOLED连接池来运行一下程序
在配置成UNPOOLED连接池来运行一下程序
然后我们去追一下UNPOOLD连接池源码
先来看看这两个配置分别对应的类
我们ctrl+n来查找一个UnpooledDataSource这个类
很明显,他就是一个实现了DataSource的类,这个DataSouce里面就有一个getConnection()方法去获得连接
下面我们说一下Pooled原理
同样实现了连接池接口
mybatis中的事务原理和自动提交设置
把下面配置成POOLED
mybatis就是通过sqlsession对象的commit方法和rollback方法实现事务的提交和回滚
找到它的实现类
然后点击去,会进入这个类
当找不到一个类,就这样做,直接展示类图
进入BaseExecutor找commit方法
进入到这个类
右键选择它的实现类
那能不能每次设置一个自动提交呢
那我们这个时候去看一个类
现在去测试一下
运行ok
动态sql
前面我们的SQL都是比较简单的,但是有时候业务逻辑复杂,我们的SQL是进行动态变化的。
拿到mybatis1进行改造,只保留查询方法与查询配置,那就先新建一个项目mybatis3,在这个里面保留查询方法与查询配置,或者不动也行
一、来说一下if标签的作用
首先在dao层增加一个这样的接口
然后在IUserDao.xml进行一个配置
在test里面测试一个方法
它的封装原理就是
运行结果:
如果我们又多加了一个参数sex="女"
二、where标签的使用
上面就是我们去简化where 后面1 == 1的这个作用
下面贴上where之后,就会给我们自动and,然后匹配条件
查询一样没问题
三、foreach和sql标签的使用
先来贴一下foreach的用法
在dao层添加一个方法
我们想要做的就是:
先去修改一下QueryVo对象里面的属性与方法
现在去配置一下IUserDao.xml里面的信息
下面去Test类里面写这个方法
运行结果:
三、下面说一下sql标签的使用
这个方法的作用是抽取sql片段
IUserDao.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">
<!--配置每一个相应dao层的方法,写上相应dao的全类名-->
<mapper namespace="com.pxx.dao.IUserDao">
<!--配置查询结果的列名和实体类的属性名对应的关系-->
<resultMap id="userMap" type="user">
<!-- 主键字段的对应 -->
<id property="userId" column="id"></id>
<!--非主键字段的对应-->
<result property="userName" column="username"></result>
<result property="userAddress" column="address"></result>
<result property="userSex" column="sex"></result>
<result property="userBirthday" column="birthday"></result>
</resultMap>
<!--
抽取重复的sql语句
-->
<sql id="defaultUser">
select * from user
</sql>
<!--Id代表方法名称 resultType:代表值类型
这里写上它的全类名 -->
<!--<select id="findAll" resultType="com.pxx.domain.User">-->
<select id="findAll" resultMap="userMap">
<!--select * from user
这里用sql标签代码替换掉
-->
<include refid="defaultUser"></include>
<!-- select id as userId,username as userName,address as userAddress,
sex as userSex,birthday as userBirthday from user-->
</select>
<!--保存用户信息就不是select语句了
而是insert语句-->
<insert id="saveUser" parameterType="user">
<!--配置插入操作之后,获取插入数据的id-->
<selectKey keyProperty="userId" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into user(username,address,sex,birthday)values(#{userName},#{userAddress},#{userSex},#{userBirthday});
</insert>
<!--这里更新操作就是update-->
<update id="updateUser" parameterType="user">
update user set username=#{userName},address=#{userAddress},sex=#{userSex},birthday=#{userBirthday} where id=#{userId}
</update>
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id=#{id}
</delete>
<!--根据id查询用户的方法,又是查询操作-->
<select id="findById" parameterType="java.lang.Integer" resultMap="userMap">
select * from user where id=#{id}
</select>
<!--根据名称进行模糊查询操作
但是模糊查询中like %名字% 中的这个百分号在在这里面体现不出来
-->
<select id="findByName" parameterType="string" resultMap="userMap">
select * from user where username like #{username}
</select>
<!--查询用户总记录数-->
<select id="findTotal" resultType="int" >
select count(id) from user
</select>
<!--根据queryVo的条件查询用户-->
<select id="findUserByVo" parameterType="com.pxx.domain.QueryVo" resultMap="userMap">
select * from user where username like #{user.userName}
</select>
<!--
根据条件来得到一个查询结果
-->
<!--<select id="findUserByCondition" resultMap="userMap" parameterType="user">
select * from user where 1=1
<if test="userName != null">
and username = #{userName}
</if>
<if test="userSex != null">
and sex = #{userSex}
</if>
</select>-->
<select id="findUserByCondition" resultMap="userMap" parameterType="user">
<!--select * from user-->
<include refid="defaultUser"></include>
<where>
<if test="userName != null">
and username = #{userName}
</if>
<if test="userSex != null">
and sex = #{userSex}
</if>
</where>
</select>
<select id="findUserInIds" resultMap="userMap" parameterType="queryvo">
<!--select * from user-->
<include refid="defaultUser"></include>
<where>
<if test="ids != null and ids.size()>0">
<!--
利用foreach来进行一个参数的封装
-->
<foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>
</mapper>
运行结果:
Mybatis的多表操作
一、下面来分析一下表与表之间的关系
看一下数据库中account表与user表之间的关系
看一下上面两张表的构建
account表
user表
上面的account中的uid与user表中的id,前者是从表,后者是主表,前者被后者约束
uid可以有多个相同的id值匹配
二、一对一查询(多对一查询)
一个user匹配了多个账户,换句话说,一个账户拿出来只会匹配一个用户
也就是多对一的一个查询,变成一对一的查询
1.先来定义一个账户信息的实体类
Account.java
package com.pxx.domain;
public class Account {
private Integer id;
private Integer uid;//这格个是从键,user表的id是主键
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 +
'}';
}
}
2.编写sql语句
实现查询账户信息时,也要查询账户所对应的用户信息。这里是查询所有信息。
现在定义一个封装类AccountUser来封装上面的数据
AccountUser.java
package com.pxx.domain;
//在账户的基础上增加的一些信息
public class AccountUser extends Account{
//增加多出来的用户名与地址
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 "AccountUser{" +
"username='" + username + '\'' +
", address='" + address + '\'' +
'}';
}
}
上面就会给我们返回一个查询结果
我们需要把这个查询结果封装到一个对象里面去
3.定义账户的持久层Dao接口
因为每一个dao基本上就是操作一张表,这里是account表,就是IAccountDao接口
4.现在来定义AccountDao.xml的配置信息
为什么说现在不需要去配置SqlMapConifg.xml中mappers中映射一个每一个dao文件的xml配置文件
因为这里已经用package进行了配置
好了,不说那么多,继续配置AccountDao.xml文件
下面开始去写测试类
运行结果
上面这种采用一个AccountUser去继承Account显示是不行的,也违背了软件的合成复用原则,它的意思就是尽量使用对象组合、聚合,而不是使用继承关系达到代码的复用的目的
下面我们采用另外一种方式来做
首先在account里面聚合一个user类,因为要查询出user类的username和address信息
换句话说,就是从表的实体应该包含一个主表实体的对象引用
在Account对象里面
现在我们去修改IAccountDao.xml文件,在修改这个.xml文件之前,我们要先去修改User.java文件
User.java
package com.pxx.domain;
import java.util.Date;
public class User {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
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 String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", address='" + address + '\'' +
", sex='" + sex + '\'' +
", birthday=" + birthday +
'}';
}
}
现在再去配置一下IAccountDao.xml中的数据封装信息
现在去dao层是写一个操作接口
IAccountDao.java
现在去相应的.xml配置文件中去配置一下
IAccountDao.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.pxx.dao.IAccountDao">
<!--定义封装的account和user表的resultmap集合
伊瓦涅
-->
<resultMap id="accountusermap" type="account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!--
一对一的一个关系映射:配置封装user的内容
以为account里面聚合的一个user类,所以
不能直接向上面一样result
-->
<association property="user" column="uid" javaType="user">
<id property="id" column="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>
</association>
</resultMap>
<!--查询所有-->
<select id="findAll" resultMap="accountusermap">
select u.*,a.id as aid,a.uid,a.money from account a,user u where u.id = a.uid
</select>
<!--配置查询所有操作
查询一个账户同时包含了用户名和地址信息
-->
<select id="findAllAccount" resultType="accountuser">
select a.*,u.username,u.address from account a,user u where u.id = a.uid
</select>
</mapper>
解释一下两个地方
下面去看一下AccountTest.java里面代码
package com.pxx.test;
import com.pxx.dao.IAccountDao;
import com.pxx.dao.IUserDao;
import com.pxx.domain.Account;
import com.pxx.domain.AccountUser;
import com.pxx.domain.QueryVo;
import com.pxx.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class AccounTest {
//把这些变量先全部做成全局变量
private InputStream in;
private SqlSession sqlSession;
private IAccountDao accountDao;
@Before
public void init() throws IOException {
//1.读取配置文件
//这个Resources是Mytais给我们提供的一个类,直接加载资源文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产SqlSession对象,这个可以理解为dao的实现类
sqlSession = factory.openSession(true);
//4.使用SqlSession创建Dao接口的代理对象
accountDao = sqlSession.getMapper(IAccountDao.class);
}
@After
public void destroy() throws IOException {
// sqlSession.commit();
sqlSession.close();
in.close();
}
/**
* 测试查询所有信息
*/
@Test
public void testFindAll() {
List<Account> accounts = accountDao.findAll();
for(Account account : accounts){
System.out.println("--------每个account的信息------------");
System.out.println(account);
System.out.println(account.getUser());
}
}
/**
* 测试查询所有账户,同时包含用户名称和地址
*/
@Test
public void testFindAllAccount() {
List<AccountUser> aus = accountDao.findAllAccount();
for(AccountUser au : aus) {
System.out.println(au);
}
}
}
运行结果:
总结关于:多对一的一个查询
三、关于一对多的查询操作
1.还是拿到这个来分析一把
2.编写sql语句
这里采用左外连接的用法
拿到左边去整体匹配一遍右表的数据,如果有匹配则显示出来,不要这条记录也显示,但是只是游右表上面相应字段的数据显示为null
3.做一对多,在主表User类中聚合一个从表Accoount类
User.java
package com.pxx.domain;
import java.util.Date;
import java.util.List;
public class User {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
private List<Account> accounts;
public List<Account> getAccount() {
return accounts;
}
public void setAccount(List<Account> account) {
this.accounts = account;
}
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 String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", address='" + address + '\'' +
", sex='" + sex + '\'' +
", birthday=" + birthday +
", accounts=" + accounts +
'}';
}
}
4.在IUserDao接口中加入方法
5.下面去相关的dao文件中进行配置IUserDao.xml
这里说一下在多对一的时候,我们在.xml文件中做了如下的相关映射
那么一对多也要做一个相关类型的配置,才能映射相关字段信息
下面重新写一个测试方法UserTest
package com.pxx.test;
import com.pxx.dao.IAccountDao;
import com.pxx.dao.IUserDao;
import com.pxx.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class UserTest {
//把这些变量先全部做成全局变量
private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;
@Before
public void init() throws IOException {
//1.读取配置文件
//这个Resources是Mytais给我们提供的一个类,直接加载资源文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产SqlSession对象,这个可以理解为dao的实现类
sqlSession = factory.openSession(true);
//4.使用SqlSession创建Dao接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After
public void destroy() throws IOException {
// sqlSession.commit();
sqlSession.close();
in.close();
}
@Test
public void testFindAllUser() {
List<User> users = userDao.findAllUser();
for(User user : users) {
System.out.println("-------每个用户的内容---------");
System.out.println(user);
System.out.println(user.getAccounts());
}
}
}
运行结果:
Mybatis多表查询之多对多
一、role与user表的多对多
有时候也是看成一对多或者多对一来考虑的
比如说角色和用户,那么一个角色,就有多个用户,1个用户也有多个角色
1.先来分析一下表与表之间的关系,多对多,这里会涉及到第三张表
2.编写具体的SQL语句
3.编写角色实体类
我们需要是是不是下面两张表的信息
所以这里其实是一个1对多的查询,这个时候类的设计我们就把从表放到主表里面
也就是把user聚合到role里面
Role.java
package com.pxx.domain;
import java.util.List;
public class Role {
private int 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 int getRoleId() {
return roleId;
}
public void setRoleId(int 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 + '\'' +
", users=" + users +
'}';
}
}
4.编写Role表的dao层接口
5、现在要给这张表的dao层准备一个.xml的配置文件
IRoleDao.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.pxx.dao.IRoleDao">
<!--
这个里面还是涉及到了一个对象包含了另外一个对象
这里就要做一个属性字段映射
当多对一的时候,采用<association>
这里一对多,就采用<collection>配置对象里面的对象
-->
<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>
<!--开始配置user类
property:映射到role类上面的饿某个属性上面
ofType:把结果集封装到哪一个类上面
-->
<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 r.*,u.id uid,u.username username,u.birthday birthday,u.sex sex,u.address address
FROM role r
INNER JOIN user_role ur ON (r.id = ur.rid)
INNER JOIN USER u ON (ur.uid = u.id)
</select>
</mapper>
6、现在开始编写测试类