1.MyBatis
MyBatis 它是Apache的一个开源项目-iBatis , 2010年这个项目由apache software foundation 迁 移到了google code,并且改名为MyBatis 2013年11月迁移到Github。
1.概述
MyBatis是一款优秀的持久化框架,支持自定义SQL,存储过程以及高级映射,MyBatis免去了几乎所有的JDBC代码 以及设置参数,获取结果集对象的工作。MyBatis可以通过简单的XML配置或注解来配置映射关系。
Hibernate框架:全自动映射ORM框架
ORM: Object Relation Mapping 对象关系映射,简单说就是把数据库表和实体类的属性对应起来,让我们可以操作实体类就可以实现操作数据库表。
1.2持久化
数据持久化
- 持久化就是将程序的数据在持久化状态和瞬时状态转化的过程
- 内存:断电及失
- 数据库(JDBC),io文件持久化
- 生活:冷藏,罐头
为什么需要持久化?
1.3持久层
Dao层,Service层,Controller层…
2.为什么使用MyBatis
- JDBC:
- SQL编写在代码中,耦合度比较高
- 实际开发中SQL会经常被更新,维护不易
- HiBernate 和 JPA
- 内部自动生成SQL,不方便特殊优化
- 长难复杂的SQL,对HiBernate来说处理很不如意
- 基于全映射的全自动框架,进行字段部分处理不叫困难,并且会导致数据库性能下降
- MyBatis
- SQL和Java编码分离,功能划分清楚,一个专注数据,一个专注业务
- 核心SQL可以自己编写,优化比较方便
3.MyBatis 的使用
3.1Mybatis环境搭建
步骤:
1.创建Maven工程,并导入相关依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wdzl</groupId>
<artifactId>HelloMyBatis</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</project>
2.创建实体类
提供get set方法 和 构造方法
3.创建dao层接口:UserDao
4.创建MyBatis主配置文件:SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="13992794421"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/wdzl/dao/UserDao.xml"></mapper>
</mappers>
</configuration>
5.创建映射配置文件:UserDao.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.wdzl.dao.UserDao">
<select id="findAll" resultType="com.wdzl.pojo.Student">
select*from student;
</select>
</mapper>
注意事项:
1.创建directory 和 package的区别
directory:com.wdzl.dao
创建的是一级目录
package:com.wdzl.dao
创建了三级目录
2.MyBatis的映射配置文件所在目录层级要与对应的dao层接口目录层级相同
3.映射配置文件mapper
标签namespace
属性的值必须对应接口的全限定类名。
4.映射配置文件select
标签id
属性必须与对应接口中的方法名一致,不能随便写。
5.只要遵循2,3,4的规定,我们就不用去写接口的实现类
3.2测试案列
步骤:
- 读取配置文件
- 创建SqlSessionFactory对象 —> 通过SqlSessionFactoryBuilder对象获取
- 使用工厂对象生产SqlSession对象
- 使用SqlSession对象获取接口的代理对象
- 使用代理对象调用方法
- 释放资源
3.3 MyBatis 注解入门
1.我们先新建一个Maven工程,将上一个工程的内容都复制到该工程中。
2.因为该案例使用的是注解方式,所以 映射配置文件就不需要了我们可以将它删除掉,并在相应的接 口方法上写上注解即可。
3.修改sqlMapConfig.xml
4.MyBatis- CRUD操作
4.1添加
需求:向User表中添加一名用户,数据自拟
1.在UserDao中添加方法
2.在UserDao.xml中配置该方法
注意:取值这里有两种方式#{} 和 ${}
-
#{} : 采取预编译方式
-
${}:采取字符串拼接
可以通过控制台日志查看
3.编写测试类
我们将前4步提 取出来放在 init() 方法中,释放资源提取到 destory() 方法中
public class UserDaoTest {
private InputStream inputStream;
private SqlSessionFactoryBuilder builder;
private SqlSessionFactory factory;
private SqlSession sqlSession;
private UserDao userDao;
@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
builder = new SqlSessionFactoryBuilder();
factory = builder.build(inputStream);
sqlSession = factory.openSession();
userDao = sqlSession.getMapper(UserDao.class);
}
@After
public void destory() throws IOException {
inputStream.close();
sqlSession.close();
}
@Test
public void findAllTest(){
List<User> users = userDao.findAll();
for(User user : users){
System.out.println(user);
}
}
@Test
public void addUserTest(){
User user = new User(0,"屈波","2000-02-18","男","安康");
userDao.addUser(user);
sqlSession.commit();
System.out.println(user);
}
}
4.2修改
需求:将用户名为刘德华的地址改为宝鸡
1.在 UserDao 接口中添加相应的方法
void updateUser(User user);
2.在UserDao.xml中进行配置
<update id="updateUser" parameterType="com.wdzl.pojo.User">
update mybatis.user set address=#{address} where username=#{username};
</update>
3.测试
@Test
public void updateUserTest(){
User user = new User();
user.setUsername("刘德华");
user.setAddress("宝鸡");
userDao.updateUser(user);
sqlSession.commit();
}
4.3删除
需求:根据username删除用户
- 在 UserDao 接口中添加相应的方法
void delUser(User user);
- 在UserDao.xml中进行配置
<delete id="delUser" parameterType="com.wdzl.pojo.User">
delete from user where username=#{username};
</delete>
- 测试
@Test
public void delUserTest(){
User user = new User();
user.setUsername("赵童");
userDao.delUser(user);
List<User> users = userDao.findAll();
for(User user3 : users){
System.out.println(user3);
}
sqlSession.commit();
}
4.4 查询一条数据
需求:根据用户id查询 用户信息
1.在 UserDao 接口中添加相应的方法
User findUserById(int id);
2.在UserDao.xml中进行配置
<select id="findUserById" parameterType="INT" resultType="com.wdzl.pojo.User">
select *from user where id=#{id};
</select>
3.测试
@Test
public void findUserByIdTest(){
User userById = userDao.findUserById(2);
System.out.println(userById);
}
4.5 根据名称模糊查询用户
需求:根据用户name模糊查询 用户信息
1.在 UserDao 接口中添加相应的方法
List<User> findUserByName(String name);
2.在UserDao.xml中进行配置
<select id="findUserByName" parameterType="String" resultType="com.wdzl.pojo.User">
select * from user where username like #{name};
</select>
3.测试
@Test
public void findUserByNameTest(){
List<User> users = userDao.findUserByName("%力%");
for (User user : users){
System.out.println(user);
}
}
4.5 使用聚合函数
需求:使用聚合函数查询总记录数
1.在 UserDao 接口中添加相应的方法
int findTotal();
2.在UserDao.xml中进行配置
<select id="findTotal" resultType="INT">
select count(id) from user;
</select>
3.测试
@Test
public void findTotalTest(){
int total = userDao.findTotal();
System.out.println(total);
}
4.6 插入数据,获取当前保存数据的自增长ID
该需求需要一条sql语句:
SELECT LAST_INSERT_ID();
1.在配置文件中,找到添加用户的配置标签,在该标签内使用 获取当前插入数据的自增 长ID
<insert id="addUser" parameterType="com.wdzl.pojo.User">
<selectKey keyProperty="id" order="AFTER" resultType="int">
select last_insert_id();
</selectKey>
insert into user(username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address});
</insert>
keyProperty
: 对应的实体类属性名
keyColumn
:对应数据库列名
Order
: 执行的顺序,after-在插入操作后执行
2.测试
@Test
public void testAdd2() throws IOException {
User user = new User(0,"刘德华",new Date(),"男","中国香港");
System.out.println(user);
userDao.addUser(user);
session.commit();
System.out.println(user);
}
保存之后的user对象的id就会变成当前数据库中实际的id
5. 参数详解
9.1 parameterType 输入类型
- 传递基本数据类型和 String
- 传递pojo对象
当传递参数为对象时,在sql语句中获取对象属性值需要使用OGNL表达式。
- OGNL:Object Graphic Navigation Language 对象图导航语言
- 它是通过对象的取值方式来获取值,在写法上把get给省略了
- 在类中,我们获取值:user.getUsername();
- 在OGNL中:user.userName
- Mybatis中为什么能直接写userName,而不加 user. 呢?
- 因为在 parameterType 中已经提供了属性所属的类,所以可以省略类名
9.2 使用包装类型作为参数类型
有时候我们的查询条件可能是符合条件,比如说查询条件需要用户信息,还需要其他信息,这些信 息可以组成一个对象,这时候我们就可以考虑使用包装类来组合条件。
- 创建包装类QueryVo
public class QueryVo {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public QueryVo() {
}
public QueryVo(User user) {
this.user = user;
}
}
- 在接口中定义相应方法
List<User> findUserByVo(QueryVo vo);
- 在配置文件UserDao.xml中进行配置
<select id="findUserByVo" parameterType="com.wdzl.pojo.QueryVo" resultType="com.wdzl.pojo.User">
select * from user where username like #{user.username}
</select>
4.测试
@Test
public void findByVoTest(){
QueryVo queryVo = new QueryVo();
User user = new User();
user.setUsername("%力%");
queryVo.setUser(user);
List<User> userByVo = userDao.findUserByVo(queryVo);
for (User user2 : userByVo){
System.out.println(user2);
}
}
9.3 实体类属性与表字段不对应的问题解决
首先我们先将 User
类中的 id 属性名改为 uid ,重写类中的方法,重新执行查询功能,我们会发现,查出来的数据,id项都为空,这是因为属性和数据库字段名无法匹配的原因,对于这些问题,我们有3种解决方案。
方案1:统一实体类和表字段的名称
方案2:给数据库字段加别名 ,让别名与实体类属性名 统一(查询效率比较高)
我们以 根据包装类信息模糊查询用户信息为例,对sql语句进行修改,并给不对应的列起一个别名。
<select id="findUserByVo" parameterType="com.wdzl.domain.QueryVo"
resultType="com.wdzl.domain.User">
select id as uid,username,birthday,sex,address from user where
username like #{user.username};
</select>
方案3:在UserDao.xml中配置对应关系
1.在配置文件UserDao.xml中添加映射配置信息
<resultMap id="userMap" type="com.wdzl.pojo.User">
<id property="uid" column="id"></id>
<result property="username" column="username"></result>
</resultMap>
<select id="findAll" resultMap="userMap">
select * from user;
</select>
6. MyBatis实现Dao层的开发
现实开发中,有时候也是需要我们自己去实现Dao层接口,那么如何使用MyBatis框架自定义实现类是我们接下来要研究的内容。
1.添加UserDao对应的实现类
package com.wdzl.dao.impl;
import com.wdzl.dao.UserDao;
import com.wdzl.pojo.QueryVo;
import com.wdzl.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import java.util.List;
public class UserDaoImpl implements UserDao {
private SqlSessionFactory factory;
private SqlSession sqlSession;
public UserDaoImpl() {
}
public UserDaoImpl(SqlSessionFactory factory){
this.factory=factory;
sqlSession = factory.openSession();
}
@Override
public List<User> findAll() {
List<User> users = sqlSession.selectList("com.wdzl.dao.UserDao.findAll");
return users;
}
@Override
public void addUser(User user) {
sqlSession.insert("com.wdzl.dao.UserDao.addUser", user);
sqlSession.commit();
sqlSession.close();
}
@Override
public void updateUser(User user) {
sqlSession.update("com.wdzl.dao.UserDao.updateUser",user);
sqlSession.commit();
sqlSession.close();
}
@Override
public void delUser(User user) {
sqlSession.delete("com.wdzl.dao.UserDao.delUser",user);
sqlSession.commit();
sqlSession.close();
}
@Override
public User findUserById(int id) {
User user = sqlSession.selectOne("com.wdzl.dao.UserDao.findUserById", id);
return user;
}
@Override
public List<User> findUserByName(String name) {
List<User> list = sqlSession.selectList("com.wdzl.dao.UserDao.findUserByName", name);
return list;
}
@Override
public int findTotal() {
Integer integert = sqlSession.selectOne("com.wdzl.dao.UserDao.findTotal");
return integert;
}
}
测试:
public class UserDaoTest {
private InputStream inputStream;
private SqlSessionFactoryBuilder builder;
private SqlSessionFactory factory;
private UserDao userDao;
@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
builder = new SqlSessionFactoryBuilder();
factory = builder.build(inputStream);
userDao = new UserDaoImpl(factory);
}
@After
public void close() throws IOException {
inputStream.close();
}
@Test
public void findAllTest(){
UserDao userDao = new UserDaoImpl(factory);
List<User> users = userDao.findAll();
for(User user : users){
System.out.println(user);
}
}
@Test
public void addUserTest(){
User user = new User(0,"王必武","2000-5-6","男","湖南");
userDao.addUser(user);
}
@Test
public void updateUserTest(){
User user = new User();
user.setUsername("唐康");
user.setAddress("宝鸡");
userDao.updateUser(user);
}
@Test
public void delUserTest(){
User user = new User();
user.setUsername("唐康");
userDao.delUser(user);
}
@Test
public void findUserByIdTest(){
User user = userDao.findUserById(20);
System.out.println(user);;
}
@Test
public void findUserByNameTest(){
List<User> users = userDao.findUserByName("%波");
for (User user : users){
System.out.println(user);
}
}
@Test
public void findTotalTest(){
int total = userDao.findTotal();
System.out.println(total);
}
}
7.标签
7.1 properties标签
**方式一:**将配置提取出来进行统一管理,在需要使用的地方使用 ${}
引用即可
1.提取
2.引用
方式二:引用外部配置文件
引用外部有两个属性:
Http : // localhost : 8080 /book/index.jsp
协议 主机地址 端口 URI
7.2 typeAliases标签 和 package标签
在主配置文件中使用 typeAliases
配置别名,它只能给实体类配置别名。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1bQD8L3M-1616665998027)(D:\图片\1.1.1.1.png)]
在 typeAliases 中有两个属性
**type:**用来指定起别名的实体类,如果省略 alias 属性,则使用默认别名,默认别名为类名 ,不区分大小写,任意组合都可识别。
**alias:**指定一个新的别名,比如上图案例中将别名设置为 hehe ,则在下面使用时使用 hehe 即可表 示User类。新的别名也不区分大小写。
当实体类比较多时,我们可以使用 package 标签批量给包中的类添加别名
8.动态SQL
8.1 if标签
**需求:**根据给定条件完成对满足条件的用户信息复合查询:查询满足名称叫刘德华 且 性别为男的所有 员工信息
<select id="findUserByConditions" parameterType="user" resultType="User">
select * from user where 1=1
<if test="username !=null">
and username=#{username}
</if>
</select>
8.2 where标签
where
标签是为了解决拼接sql时需要添加一个恒等式的问题
<select id="findUserByConditions" parameterType="user" resultType="user">
select * from user
<where>
<if test="username !=null and username!=''">
and username=#{username}
</if>
<if test="sex!=null and sex!=''">
and sex=#{sex}
</if>
</where>
</select>
8.3 foreach 标签
当我们要查询满足条件在某个范围中的信息时:
SQL:
select * from user where id in (1,3,5,7,9);
1.UserDao 添加相应方法并在映射文件中进行配置
List<User> findUserByConditions(User user);
List<User> findUserByIds(QueryVo vo);
<select id="findUserByConditions" parameterType="user" resultType="user">
select * from user
<where>
<if test="username != null">
and username = #{username}
</if>
<if test="sex != null">
and sex = #{sex}
</if>
<if test="address != null">
and address = #{address}
</if>
</where>
</select>
<select id="findUserByIds" parameterType="queryvo" resultType="user">
select * from user
<where>
<if test="ids != null and ids.size()>0">
<foreach collection="ids" open="and id in (" close=")" item="id" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
</mapper>
collection:遍历的集合
**open:**拼接SQL以该属性值为开头
**close:**拼接SQL以该属性值为结尾
item:变量名,相当于增强for循环中的那个变量。拼接在 open 和 close 之间
**separator:**元素(变量)间以什么分割
#{变量名}:必须与 item 属性保持一致。
9.MyBatis 的 多表查询
在MySQL中 表与表之间的关系
举例:
- 一对一
- 人 和 身份证,一个人对应一个身份证,一个身份证对应一个人
- 一对多(多对一):
- 班级和学生,一个班级对应多个学生,一个学生只能对应一个班级。这种关系就是一对多(多对 一)的关系。
- 多对多
- 学生和老师:一个学生可以有多个老师,一个老师可以有多个学生
- 特殊
- 如果将订单取出,这个订单只能属于一个用户, 所以,在MyBatis中, 就把多对一的关系按照一对一的情况进行处理。
9.1MyBatis中的多表查询 -1多对一,一对多
准备工作:Module创建 并能实现单表信息查询
1.数据库表:用户表 和 账户表
CREATE DATABASE mybatis2 CHARSET utf8;
USE mybatis2;
CREATE TABLE `user` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(64) NOT NULL COMMENT '用户名称',
`birthday` DATETIME DEFAULT NULL COMMENT '生日',
`sex` CHAR(1) DEFAULT NULL COMMENT '性别',
`address` VARCHAR(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user`(`id`,`username`,`birthday`,`sex`,`address`)
VALUES
(NULL,'老王','2017-05-27 18:27:09','男','北京'),
(NULL,'小王','2019-04-03 15:09:37','女','西安'),
(NULL,'小李','2019-07-22 17:44:33','女','咸阳'),
(NULL,'小刘','2018-03-04 15:52:36','男','渭南'),
(NULL,'大王','2016-11-03 17:37:26','男','富平'),
(NULL,'小马','2016-04-09 01:24:40','女','上海');
CREATE TABLE `account` (
`ID` INT(11) NOT NULL COMMENT '编号',
`UID` INT(11) DEFAULT NULL COMMENT '用户编号',
`MONEY` DOUBLE DEFAULT NULL COMMENT '金额',
PRIMARY KEY (`ID`),
KEY `FK_Reference_8` (`UID`),
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `account`(`ID`,`UID`,`MONEY`) VALUES (1,2,1000),(2,1,1000),
(3,2,2000);
2.创建主配置文件:SqlMapConfig.xml,并且将log4j,jdbc的配置文件一起复制到 resources 目录下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties"></properties>
<typeAliases>
<package name="com.wdzl.pojo"/>
</typeAliases>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<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>
</environment>
</environments>
<mappers>
<mapper resource="com/wdzl/dao/UserDao.xml"/>
<mapper resource="com/wdzl/dao/AccountDao.xml"/>
</mappers>
</configuration>
3.建立两个实体类:用户实体类 和 账户实体类
User:
public class User {
private int uid;
private String username;
private Date birthday;
private char sex;
private String address;
private List<Account> accounts;
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
public User(int uid, String username, Date birthday, char sex, String address) {
this.uid = uid;
this.username = username;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
public User() {
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
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 char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"uid=" + uid +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex=" + sex +
", address='" + address + '\'' +
", accounts=" + accounts +
'}';
}
}
Account:
public class Account {
private int aid;
private int uid;
private Double money;
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Account(int aid, int uid, Double money) {
this.aid = aid;
this.uid = uid;
this.money = money;
}
public Account() {
}
@Override
public String toString() {
return "Account{" +
"aid=" + aid +
", uid=" + uid +
", money=" + money +
", user=" + user +
'}';
}
public int getAid() {
return aid;
}
public void setAid(int aid) {
this.aid = aid;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
}
4.创建各自实体类操作的接口
UserDao
public interface UserDao {
List<User> findAll();
}
AccountDao
public interface AccountDao {
List<Account> findAll();
}
5.创建各接口对应的映射配置文件
UserDao.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.wdzl.dao.UserDao">
<resultMap id="userAccountMap" type="user">
<id property="uid" column="uid"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<collection property="accounts" ofType="com.wdzl.pojo.Account" column="uid">
<id property="aid" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="userAccountMap">
SELECT * FROM USER u LEFT OUTER JOIN account a ON u.uid = a.uid;
</select>
</mapper>
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.wdzl.dao.AccountDao">
<resultMap id="accountUserMap" type="account">
<id property="aid" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<association property="user" column="uid" javaType="com.wdzl.pojo.User">
<id property="uid" column="uid"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
</association>
</resultMap>
<select id="findAll" resultMap="accountUserMap">
SELECT a.aid,a.money,u.uid,u.username,u.sex,u.address FROM account a , USER u WHERE a.uid = u.uid
</select>
</mapper>
6.将映射配置文件添加到主配置文件中
<mappers>
<mapper resource="com/wdzl/dao/UserDao.xml"/>
<mapper resource="com/wdzl/dao/AccountDao.xml"/>
</mappers>
7.测试:分别测试UserDao 和 AccountDao
public class UserTest {
private InputStream is;
private SqlSessionFactoryBuilder builder;
private SqlSessionFactory factory;
private SqlSession session;
private UserDao userDao;
@Before
public void init()throws IOException {
is = Resources.getResourceAsStream("SqlMapConfig.xml");
builder = new SqlSessionFactoryBuilder();
factory = builder.build(is);
session = factory.openSession(true);
userDao = session.getMapper(UserDao.class);
}
@After
public void destroy() throws IOException {
session.close();
is.close();
}
@Test
public void testFindAllUser() {
List<User> all = userDao.findAll();
for (User user : all) {
System.out.println(user);
}
}
}
public class AccountTest {
private InputStream is;
private SqlSessionFactoryBuilder builder;
private SqlSessionFactory factory;
private SqlSession session;
private AccountDao accountDao;
@Before
public void init()throws IOException {
is = Resources.getResourceAsStream("SqlMapConfig.xml");
builder = new SqlSessionFactoryBuilder();
factory = builder.build(is);
session = factory.openSession(true);
accountDao = session.getMapper(AccountDao.class);
}
@After
public void destroy() throws IOException {
session.close();
is.close();
}
@Test
public void testFindAllUser() {
List<Account> all = accountDao.findAll();
for (Account account : all) {
System.out.println(account);
}
}
}
9.2MyBatis中的多表查询-2 多对多
我们知道SQL中 多对多的关系需要借助一张中间。
需求:查询角色信息的同时显示赋予该角色的用户信息
步骤:
- 建立两张表,用户表 和 角色表
- 让用户表和角色表具有多对多的关系。需要使用中间表,中间表中包含这两张表的主键,并且这两个主键在中间表中是外键
- 建立两个实体类:用户实体类 和 角色实体类
- 让用户和角色实体类能体现出多对多的关系:各自包含对方的一个集合引用
- 建立配置文件
- 实现配置
- 查询用户时,可以同时得到该用户所包含的角色信息。
- 当我们查询角色时,可以同时得到拥有该角色的用户信息。
实现:先实现单表查询,再实现多对多关系查询
1.新建Module, 修改pom.xml,添加相应依赖
2.将多表查询1的Module中 src下的内容拷贝到当前Module中
3.将 Account有关的类,接口 ,配置文件等进行删除
- 实体类
- 接口
- AccountDao配置文件
- User实体类中与Account有关的属性和方法
- UserDao.xml中有关Account的配置信息
- 测试类:Account测试类 和 User测试类中有关Account的部分
4.创建数据库表并插入数据
CREATE TABLE `role` (
`rid` INT(11) NOT NULL COMMENT '编号',
`ROLE_NAME` VARCHAR(30) DEFAULT NULL COMMENT '角色名称',
`ROLE_DESC` VARCHAR(60) DEFAULT NULL COMMENT '角色描述',
PRIMARY KEY (`rid`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `role`(`rid`,`ROLE_NAME`,`ROLE_DESC`) VALUES (1,'院长','管理整个学
院'),(2,'总裁','管理整个公司'),(3,'校长','管理整个学校');
CREATE TABLE `user_role` (
`UID` INT(11) NOT NULL COMMENT '用户编号',
`RID` INT(11) NOT NULL COMMENT '角色编号',
PRIMARY KEY (`UID`,`RID`),
KEY `FK_Reference_10` (`RID`),
CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`rid`),
CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `user` (`uid`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user_role`(`UID`,`RID`) VALUES (1,1),(3,1),(1,2);
5.创建实体类
public class Role {
private int rid;
private String role_name;
private String role_desc;
private List<User> users;
public Role(int rid, String role_name, String role_desc) {
this.rid = rid;
this.role_name = role_name;
this.role_desc = role_desc;
}
public Role() {
}
public int getRid() {
return rid;
}
public void setRid(int rid) {
this.rid = rid;
}
public String getRole_name() {
return role_name;
}
public void setRole_name(String role_name) {
this.role_name = role_name;
}
public String getRole_desc() {
return role_desc;
}
public void setRole_desc(String role_desc) {
this.role_desc = role_desc;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
@Override
public String toString() {
return "Role{" +
"rid=" + rid +
", role_name='" + role_name + '\'' +
", role_desc='" + role_desc + '\'' +
", users=" + users +
'}';
}
}
public class User {
private int uid;
private String username;
private Date birthday;
private char sex;
private String address;
private List<Role> roles;
public User(int uid, String username, Date birthday, char sex, String address) {
this.uid = uid;
this.username = username;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
public User() {
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
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 char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "User{" +
"uid=" + uid +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex=" + sex +
", address='" + address + '\'' +
", roles=" + roles +
'}';
}
}
public class UserRole {
private int uid;
private int rid;
public UserRole(int uid, int rid) {
this.uid = uid;
this.rid = rid;
}
public UserRole() {
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public int getRid() {
return rid;
}
public void setRid(int rid) {
this.rid = rid;
}
@Override
public String toString() {
return "UserRole{" +
"uid=" + uid +
", rid=" + rid +
'}';
}
}
6.创建Dao接口
public interface RoleDao {
List<Role> findAll();
}
public interface UserDao {
List<User> findAll();
}
7.创建映射配置文件
RoleDao.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.wdzl.dao.RoleDao">
<resultMap id="roleMap" type="com.wdzl.pojo.Role">
<id property="rid" column="rid"></id>
<result property="role_name" column="ROLE_NAME"></result>
<result property="role_desc" column="ROLE_DESC"></result>
<collection property="users" ofType="com.wdzl.pojo.User" column="uid">
<id property="uid" column="uid"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="roleMap">
SELECT r.*,u.* FROM role r
LEFT OUTER JOIN user_role ur ON r.`rid`=ur.`RID`
LEFT OUTER JOIN USER u ON u.`uid`=ur.`UID`
</select>
</mapper>
UserDao.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.wdzl.dao.UserDao">
<resultMap id="userMap" type="user">
<id property="uid" column="uid"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<collection property="roles" ofType="com.wdzl.pojo.Role" column="rid">
<id property="rid" column="rid"></id>
<result property="role_name" column="ROLE_NAME"></result>
<result property="role_desc" column="ROLE_DESC"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="userMap">
SELECT u.*,r.* FROM USER u
LEFT OUTER JOIN user_role ur ON u.`uid`=ur.`UID`
LEFT OUTER JOIN role r ON r.`rid`=ur.`RID`
</select>
</mapper>
8.主配置文件中添加相应的映射文件配置
<mappers>
<mapper resource="com/wdzl/dao/UserDao.xml"/>
<mapper resource="com/wdzl/dao/RoleDao.xml"/>
</mappers>
9.测试
public class RoleDaoTest {
private InputStream resourceAsStream;
private SqlSessionFactory factory;
private SqlSession sqlSession;
private RoleDao roleDao;
@Before
public void init() throws IOException {
resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
factory = builder.build(resourceAsStream);
sqlSession = factory.openSession();
roleDao = sqlSession.getMapper(RoleDao.class);
}
@After
public void destroy() throws IOException {
resourceAsStream.close();
sqlSession.close();
}
@Test
public void findAllTest(){
List<Role> roles = roleDao.findAll();
for (Role role : roles){
System.out.println(role);
}
}
}
public class UserDaoTest {
private InputStream resourceAsStream;
private SqlSessionFactory factory;
private SqlSession sqlSession;
private UserDao userDao;
@Before
public void init() throws IOException {
resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
factory = builder.build(resourceAsStream);
sqlSession = factory.openSession();
userDao = sqlSession.getMapper(UserDao.class);
}
@After
public void destroy() throws IOException {
resourceAsStream.close();
sqlSession.close();
}
@Test
public void findAllTest(){
List<User> users = userDao.findAll();
for (User user : users){
System.out.println(user);
}
}
}
10.延迟加载
首先我们先来分析一个问题:
问题:
- 在一对多的关系中,我们有一个用户,他有1000个账户。
- 在查询用户时,要不要把关联的账户查出来?
- 在查询账户时,要不要把关联的用户查出来?
图解:
- 问题1:如果查出来,这一千个账户就会对应一千个账户对象,但是这些对象对于我们查询用户信 息,无疑是没有用的,这样就造成了资源的浪费
所以:用户下的账户信息应该是什么时候使用,什么时候查询
所以:账户所属用户信息应该随着账户的查询一并查出来
针对上述两个问题,我们引出两个概念:延迟加载和立即加载
延迟加载:在真正使用的时候才发起查询,不用的时候不查询。延迟加载也可以称为按需加载或 懒加 载。
立即加载:无论数据是否使用,只要调用方法,就会立马执行查询
在多表关系中:
一对多,多对多:通常情况下我们对 “多” 的一方采用延迟加载
多对一,一对一:通常情况下我们对 “一” 的一方采用立即加载
11.缓存
概述:
存在于内存中的数据叫做缓存
应用:
减少和数据库的交互,提高执行效率
适用:
不适用:
分类
MyBatis中分为 一级缓存 和 二级缓存
11.1 一级缓存
概述:
它指的是MyBatis中SqlSession对象的缓存。当我们执行查询后,查询结果会存入到SqlSession为我 们提供的一款存储区域中。该区域的存储结构为Map。当我们再次查询同样数据时,MyBatis首先去一 级缓存中查询,如果存在,则直接拿取使用
注意:
当SqlSession对象消失时,MyBatis的一级缓存也会消失
注意:
当sqlSession调用 add , update , delete,commit , close方法时,MyBatis会自动清空一级缓存,防止缓存数据与数据库中的数据不一致。
11.2 二级缓存
概述:
它指的是MyBatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的 SqlSession对象共享二级缓存。
12. MyBatis的注解开发
注意:
注解开发是为了简化映射配置文件,但是主配置文件还是存在。
环境搭建 :
-
新建Module
-
修改pom.xml,添加相应的依赖
-
创建实体类User
-
创建主配置文件:SqlSessionConfig.xml
- 引入外部配置文件
- 配置别名
- 配置数据源
- 指定带有注解的dao层接口所在位
-
创建UserDao
- 声明findAll()方法
- 在该方法上使用注解@Select
-
测试
- 读取配置文件
- 获取SqlSessionFactory对象
- 使用Factory生成SqlSession
- 使用SqlSession生成代理对象
- 调用方法
- 释放资源
-
注意事项:
采用注解开发时,如果对应目录下还存在配置文件,及时在主配置文件中没有配置 xml ,运行时也会报错。
注解分类:
MyBatis针对CRUD操作一共有四个注解
- @Select
- @Insert
- @Delete
- @Update
12.1注解方式-CRUD操作
UserDao
public interface UserDao {
@Select("select * from user")
List<User> findAll();
@Select("select * from user where uid=#{id}")
User findUserById(@Param("id") int id);
@Insert("insert into user(username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})")
void addUser(User user);
@Update("update user set address=#{address} where uid=#{uid}")
void updateUser(User user);
@Delete("delete from user where uid=#{id}")
void delUser(@Param("id") int id);
}
测试
public class UserDaoTest {
private InputStream resourceAsStream;
private SqlSessionFactory factory;
private SqlSession sqlSession;
private UserDao userDao;
@Before
public void init() throws IOException {
resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
factory = builder.build(resourceAsStream);
sqlSession = factory.openSession();
userDao = sqlSession.getMapper(UserDao.class);
}
@After
public void destroy() throws IOException {
resourceAsStream.close();
sqlSession.close();
}
@Test
public void findAllTest(){
List<User> users = userDao.findAll();
for (User user : users){
System.out.println(user);
}
}
@Test
public void findUserTest(){
User userById = userDao.findUserById(1);
System.out.println(userById);
}
@Test
public void addUserTest(){
Date date =new Date();
char s = '男';
userDao.addUser(new User(0,"你好",date,s,"中国"));
sqlSession.commit();
}
@Test
public void updateUserTest(){
User user = new User();
user.setUid(7);
user.setAddress("西安");
userDao.updateUser(user);
sqlSession.commit();
}
@Test
public void delUserTest(){
userDao.delUser(7);
sqlSession.commit();
}
}
12.2 注解方式-其他操作
@Select("select * from user where id=#{id}")
User findUserById(Integer id);
@Select("select * from user where username like #{username}")
List<User> findUserByName(String name);
@Select("select count(*) from user")
int findTotal();
12.3 处理字段名与属性名不同问题
通过 @Results
和 @Rusult
来处理不同问题
13.连接池
13.1 概述
连接池就是用于存储连接对象的一个容器,当系统初始化完成后,容器被创建,容器会申请一些连 接对象,当用户访问数据库时,从容器中获取连接对象即可。访问完之后,会将连接对象归还给连接 池。这样做可以提高效率,提高连接对象的复用,不必频繁的创建和释放连接对象。
13.2 MyBatis连接池
连接池有很多种,最为熟悉的比如c3p0,DBCP,druid等。
MyBatis支持三种内置的数据源类型:
- POOLED: 实现dataSource接口,并且使用了池的思想。
- UNPOOLED: 同样也是实现了dataSource接口,但是该类型并没有使用池的思想。
- JDNI: 采用服务器提供的JDNI技术实现的,并且在不同服务器之间获取的连接池是不一样的。
- 当前框架教学使用的是Tomcat服务器,采用的是dbcp连接池技术
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)