mybaits实现对数据库的整合操作
1. Mybatis概述
1.1 Mybatis概念
MyBatis 本是 apache 的一个开源项目 iBatis, 2010 年这个项目由 apache software foundation 迁移到了 google code,并且改名为MyBatis 。2013 年 11 月迁移到 Github。iBATIS 一词来源于“internet”和“abatis”的组合,是一个基于 Java 的持久层框架。iBATIS 提供的持久层框架包括 SQL Maps 和 Data Access Objects(DAO)。
Mybatis 基于java的持久层框架,它的内部封装了JDBC,让开发人员只需要关注SQL语句本身,不需要花费精力在驱动的加载、连接的创建、Statement的创建等复杂的过程。
Mybatis通过XML或注解的方式将要执行的各种的statement配置起来,并通过java对象和statement中的sql的动态参数进行映射生成最终执行的SQL语句,最后由mybatis框架执行SQL,并将结果直接映射为java对象。采用了ORM思想解决了实体类和数据库表映射的问题。对JDBC进行了封装,屏蔽了JDBCAPI底层的访问细节,避免我们与jdbc的api打交道,就能完成对数据的持久化操作。
O—Object java对象
R- Relation 关系,就是数据库中的一张表
M-mapping 映射
1.2 回忆 JDBC编程
1 | public class TestJDBC { |
1.3 Mybatis解决的问题
1、数据库连接的创建、释放连接的频繁操作造成资源的浪费从而影响系统的性能。
2、SQL语句编写在代码中,硬编码(代码写在项目代码中)造成代码不容易维护,实际应用中SQL语句变化的可能性比较大,一旦变动就需要改变java类。
3、使用preparedStatement的时候传递参数使用占位符,也存在硬编码,因为SQL语句变化,必须修改源码。
4、对结果集的解析中也存在硬编码。
2. Mybatis入门案例
2.1 创建数据库和表
1 | CREATE TABLE `team` ( |
2.2 创建maven项目,添加Mybatis的jar依赖
1 | <dependencies> |
2.3 编写Mybatis的配置文件
一般情况下:配置文件的名称可以自定义,课程中使用mybatis.xml。配置文件放置在java/resources中。
头文件去官网中复制粘贴。Mybatis网址
1 |
|
2.4 编写实体类
实体类中的属性必须与表中的列名保持一致
1 | package com.kkb.pojo; |
2.5 编写ORM映射文件
我们是针对实体类Team.java和表Team进行ORM映射.
Mybatis框架中,ORM映射是针对SQL语句进行,Mybatis框架将SQL语句抽取到了XML中。所以我们需要针对每个实体类编写XML映射文件。
- XML映射文件必须与实体类在同一个包下面
- XML映射文件名称必须是实体类名称一致
头文件在网站复制即可。
映射文件:实体类与数据库表的映射,SQL语句从代码的硬编码中抽取出来了
namespace=”名称必须与映射的类的名字一致,是完全限定名
id=”自定义名称,id不能重复;相当于dao中的方法名称”
resultType=”使用的要求:实体类中的属性名与表中的列名一致”
1 |
|
2.6 将映射文件注册到mybatis的配置xml文件中
1 |
|
2.7 配置映射文件的扫描位置
pom.xml文件配置映射文件的扫描路径
1 | <build> |
2.8 使用Mybatis框架的核心接口测试
1 | public class TeamTest { |
2.9入门案例的增删改查
根据ID查询单个对象
Team.xml的映射文件中添加:根据ID查询
parameterType=”参数的类型”,目前只支持一个参数,可以省略,框架可以自动判断
where teamId=#{id}: #{id}表示参数 id-自定义,只需要符合命名规范即可,没有实际对应意义1
2
3
4
5
6
7
8<!--
根据ID查询
parameterType="参数的类型",目前只支持一个参数
where teamId=#{id}: #{id}表示参数 id-自定义,只需要符合命名规范即可,没有实际对应意义
-->
<select id="findById" parameterType="int" resultType="com.kkb.pojo.Team">
select * from team where teamId=#{id}
</select>测试类中添加如下内容:
@Before注解下的before方法:执行在测试方法前,构建连接,创建sql工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19private SqlSession sqlSession;
public void testFindById(){
Team team=sqlSession.selectOne("com.kkb.pojo.Team.queryById",1001);
System.out.println(team);
}
//表示该方法在执行测试方法之前先执行
public void before() throws IOException {
//1、读取mybatis的配置文件
Reader reader = Resources.getResourceAsReader(resource) ;
//2、创建SqlSessionFactory对象,目的是获取sqlSession--根据图纸创建工厂
SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(reader);
//3、创建可以执行SQL语句的SqlSession--工厂创建产品
sqlSession = sqlSessionFactory.openSession();
}
public void after(){
sqlSession.close();
}增删改
Team.xml的映射文件中添加:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<!--删除一个球队 -->
<delete id="del" >
delete from team where teamId=#{id}
</delete>
<!--更新一个球队 -->
<update id="update" parameterType="com.kkb.pojo.Team">
update team set teamName=#{teamName},location=#{location}
where teamId=#{teamId}
</update>
<!--添加一个球队
parameterType="com.kkb.pojo.Team" 将对象作为参数,
#{值} 值必须是实体类中的属性名称,其实就是占位符?
-->
<insert id="add" parameterType="com.kkb.pojo.Team" >
INSERT INTO `team` (`teamName`, `location`, `createTime`)
VALUES (#{teamName}, #{location}, #{createTime})
</insert>测试类中添加如下方法:
sqlSession.commit();//手动提交事务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public void testDel(){
int num = sqlSession.delete("com.kkb.pojo.Team.del", 1049);
sqlSession.commit();
System.out.println(num);
}
public void testUpdate(){
Team team=sqlSession.selectOne("com.kkb.pojo.Team.queryById",1049);
team.setTeamName("linawang的球队");
team.setLocation("洛杉矶");
int num = sqlSession.update("com.kkb.pojo.Team.update", team);
sqlSession.commit();
System.out.println(num);
}
public void testAdd(){
Team team=new Team();
team.setTeamName("lina的球队");
team.setLocation("北京");
team.setCreateTime(new Date());
int num = sqlSession.insert("com.kkb.pojo.Team.add", team);//增删改必须手动提交事务
sqlSession.commit();//手动提交事务
System.out.println(num);
}
3. Mybatis对象分析
Resources
Resources 类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象。SqlSessionFactoryBuilder
SqlSessionFactory 的 创 建 , 需 要 使 用 SqlSessionFactoryBuilder 对 象 的 build() 方 法 。 事实上使用SqlSessionFactoryBuilder的原因是将SqlSessionFactory这个复杂对象的创建交由Builder来执行,也就是使用了建造者设计模式。建造者模式: 又称生成器模式,是一种对象的创建模式。 可以将一个产品的内部表象与产品的生成过程分割开来, 从而可以使一个建造过程生成具有
不同的内部表象的产品(将一个复杂对象的构建与它的表示分离, 使得同样的构建过程可以创建不同的表示). 这样用户只需指定需要建造的类型就可
以得到具体产品,而不需要了解具体的建造过程和细节.
在建造者模式中,角色分指导者(Director)与建造者(Builder): 用户联系指导者, 指导者指挥建造者, 最后得到产品. 建造者模式可以强制实行
一种分步骤进行的建造过程。SqlSessionFactory
SqlSessionFactory 接口对象是一个重量级对象(系统开销大的对象),是线程安全的,所以一个应用只需要一个该对象即可。创建
SqlSession 需要使用 SqlSessionFactory 接口的的 openSession()方法。默认的 openSession()方法没有参数,它会创建有如下特性的 SqlSession:
1、会开启一个事务(也就是不自动提交)。
2、将从由当前环境配置的 DataSource 实例中获取 Connection 对象。事务隔离级别将会使用驱动或数据源的默认设置。
3、预处理语句不会被复用,也不会批量处理更新。
openSession(true):创建一个有自动提交功能的 SqlSession
openSession(false):创建一个非自动提交功能的 SqlSession,需手动提交
openSession():同 openSession(false)SqlSession
SqlSession 接口对象用于执行持久化操作。一个 SqlSession 对应着一次数据库会话,一次会话以SqlSession 对象的创建开始,以
SqlSession 对象的关闭结束。
SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其 close()方法,将其关闭。再次需要会话,再次创建。
SqlSession 在方法内部创建,使用完毕后关闭。
SqlSession 类中有超过 20 个方法,我们常用的几乎都是执行语法相关的方法。
这些方法被用来执行定义在 SQL 映射的 XML 文件中的 SELECT、INSERT、UPDATE 和 DELETE 语句。它们都会自行解释,每一句都使用语
句的 ID 属性和参数对象,参数可以是原生类型(自动装箱或包装类)、JavaBean、POJO 或 Map。1
2
3
4
5
6
7
8
9
10
11<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)
<!--
selectOne 和 selectList 的不同仅仅是 selectOne 必须返回一个对象或 null 值。如果返回值多于一个,那么就会抛出异常。
selectMap 稍微特殊一点,因为它会将返回的对象的其中一个属性作为 key 值,将对象作为 value 值,从而将多结果集转为 Map 类型值。因为
并不是所有语句都需要参数,所以这些方法都重载成不需要参数的形式。
-->
4. 日志
4.1 添加jar依赖
1 | <dependency> |
4.2 添加日志配置文件
在resource下添加log4j.properties配置文件
DEBUG info warning error:筛选日志的几个等级,从松到严
1 | # Global logging configuration info warning error |
4.3 在mybatis配置文件中添加日志的配置
1 | <configuration> |
4.4 结果
5. 使用原有的Dao方式开发
5.1 创建工具类
5.1.1 ThreadLocal
ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为
ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,
是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。
示例:
分别创建threadlocal和list的对象
创建两个
运行结果:
可以看到ThreadLocal保存的内容,在两个线程中是相互独立的副本,没有关联
list保存的内容被线程共享
1 | class Test{ |
5.1.2 工具类
1 | package com.kkb.util; |
5.1.3 创建TeamDao接口和实现类
1 | ``` |
创建TeamMapper.xml文件, 与Team.xml内容几乎一样,只有namespace=”com.kkb.mapper.TeamMapper”修改为接口的完全限定
在mybatis.xml配置文件中注册映射文件
1
2
3
4<mappers>
<!--省略其他-->
<mapper resource="com/kkb/mapper/TeamMapper.xml"/>
</mappers>getMapper方法获取代理对象
只需调用 SqlSession 的 getMapper()方法,即可获取指定接口的实现类对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31public class TestTeamMapper {
//通过动态代理的方式产生实现类
private TeamMapper teamMapper= MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
public void testAdd(){
Team team=new Team();
team.setLocation("杭州");
team.setTeamName("开课吧球队1");
team.setCreateTime(new Date());
int add = teamMapper.add(team);
MybatisUtil.getSqlSession().commit();
System.out.println(add);
System.out.println("新增的球队ID:"+team.getTeamId());
}
public void testQueryByID(){
TeamMapper teamMapper= MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
Team team = teamMapper.queryById(1025);
System.out.println(team);
}
public void testDel(){
TeamMapper teamMapper= MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
int del = teamMapper.del(1045);
MybatisUtil.getSqlSession().commit();
System.out.println(del);
}
}
7、增删改查中的细节
7.1 插入数据的时候获取自增的id
字符串的自增:
order属性的before:表示selectkey里的语句先于insert语句执行
1 | <mapper namespace="com.kkb.mapper.GameRecordMapper"> |
7.2 4种传参方式
接口文件:
1 | public interface TeamMapper { |
xml文件中:
1 | <!--细节1: |
测试代码:
1 | private TeamMapper teamMapper= MybatisUtil.getSqlSession().getMapper(TeamMapper.class); |
7.3 #{} 和 ${}的区别
{}:表示一个占位符,通知Mybatis 使用实际的参数值代替。并使用 PrepareStatement 对象执行 sql 语句, #{…}代替sql 语句的“?”。这个是Mybatis 中的首选做法,安全迅速。
\${}:表示字符串原样替换,通知Mybatis 使用\$包含的“字符串”替换所在位置。使用 Statement或者PreparedStatement 把 sql 语句和\${}的内容连接起来。一般用在替换表名,
列名,不同列排序等操作。
方式1:
TeamMapper接口添加如下内容:
1 | List<Team> queryByName(String teamName); |
TeamMapper.xml配置文件中添加如下:
1 | <select id="queryByName" resultType="com.kkb.pojo.Team"> |
测试类添加方法:
1 |
|
方式2:使用不同列作为查询条件
TeamMapper接口添加如下内容:
1 | List<Team> queryByFiled(@Param("column") String column,@Param("columnValue") String columnValue); |
TeamMapper.xml配置文件中添加如下:
1 | <select id="queryByFiled" resultType="com.kkb.pojo.Team"> |
测试类添加方法
1 |
|
根据xml和测试方法可以明显看出差异:
方式1:两个xml语句,实现不同字段的查询
方式2: 一个xml语句,实现不同字段的查询,\${}可以表示任意字段
7.4 输出映射
7.4.1 resultType
对应调用该条sql语句的方法的返回值类型
输出简单类型
案例:返回球队的总记录数
TeamMapper接口添加如下内容:1
int getCount();
TeamMapper.xml配置文件中添加如下:
1
2
3
4
5
6<!-- 只有返回的结果是单行的时候,返回值类型才可以指定为基本类型
如果是单行多列,也取不到后面的列的值;
如果返回多行会报异常:TooManyResultsException-->
<select id="getCount" resultType="java.lang.Integer">
select count(teamId) from team
</select>测试方法:
1
2
3
4
5
public void test07(){
int count = teamMapper.getCount();
System.out.println("总共的行数:"+count);
}输出pojo类型
参考之前的查询所有球队信息:
1
List<Team> queryAll();
1
2
3
4<!--接口方法返回是集合类型,但是映射文件中的resultType需要指定集合中的类型,不是集合本身。-->
<select id="queryAll" resultType="com.kkb.pojo.Team">
select * from team;
</select>输出map类型
当我们只需要查询表中几列数据的时候可以将sql的查询结果作为Map的key和value。一般使用的是Map
案例:根据id查询球队名称和位置。
TeamMapper接口添加如下内容
1
2Map<Object,Object> queryTwoColumn(int teamId);
List<Map<Object,Object>> queryTwoColumnList();TeamMapper.xml配置文件中添加如下:
1
2
3
4
5
6<select id="getTwoColumn" resultType="hashmap">
select count(teamId) as 'sum',max(teamId) as 'max' from team
</select>
<select id="getTwoColumnList" resultType="java.util.HashMap">
select count(teamId) as 'sum',max(teamId) as 'max' from team group by location;
</select>测试类添加方法:
1
2
3
4
5
6
7
8
9
10
11
12
public void test08(){
Map<String, Object> map = teamMapper.queryTwoColumn();
System.out.println(map);
}
public void test09(){
List<Map<String, Object>> list = teamMapper.queryTwoColumnList();
for (Map<String, Object> map : list) {
System.out.println(map);
}
}
7.4.2 resultmap
创建resultMap:相当于自己编写表中的列与实体类中的属性的映射
id:resultMap的名称,要求唯一
type:期待要映射为java的类型
1 | <!--创建resultMap:相当于自己编写表中的列与实体类中的属性的映射 |
9. 关系映射
9.1 对一关系的映射
实体类
1 | public class Player { |
mapper接口
1 | public interface PlayerMapper { |