Mybatis架构

1.1.1.Mybatis 应用架构(重点)
谈谈对mybatis的应用架构的理解?
MyBatis 是一个优秀的持久层框架,实现了对JDBC操作(标准API)的封装,主要用于传统简化JDBC操作中的一些相对繁琐的步骤,例如参数的映射,结果集的映射(数据库中记录存储到内存中的对象中)等。
Mybatis架构_第1张图片

为何使用mybatis实现数据持久层应用?
1)第一稳定,灵活(动态SQL),功能强大(池,日志,缓存)
2)学习以及使用成本低
3)解耦,SQL的可维护性,可复用性比较高。

说明:mybatis官网参考
1)mybatis.org/mybatis-3
2)github.com/mybatis
3)http://www.mybatis.org/spring/

1.1.2.Mybatis 产品架构
说说对mybatis的产品架构的理解?
所有框架都要解决一些共性问题(持久化),都是一种半成品,mybatis也不例外,它作为一种框架,它要解决相关问题,如何解决问题?采用怎样的架构解决问题,这是我们要学习的一个点。
Mybatis架构_第2张图片
思考:mybatis作为一个持久层框架,应该解决哪些功能性问题?
1)会话功能 (SqlSession)
2)会话语言 (SQL,动态SQL)
3)会话协议 (TCP)
4)用户体验 (连接池,缓存,日志)

1.1.3.MyBatis 技术架构
Mybatis 框架"构成"分析,如下图所示:
Mybatis架构_第3张图片
资源配置技术架构,如下图所示:
Mybatis架构_第4张图片
会话工厂对象创建(说说SqlSessionFactory对象的创建过程),如下图所示:
Mybatis架构_第5张图片
会话对象创建?(说说sqlsession对象创建过程),如下图所示:
Mybatis架构_第6张图片
会话对象应用方式,如下图所示:
Mybatis架构_第7张图片
1.2.MYBATIS 环境配置实践
1.2.1.初始化数据环境
本次数据初始化直接在mysql的命令行执行:
1.启动系统命令行控制台
2.登陆数据库:mysql -u root -p
3.设置客户端编码:set names utf8
4.导入数据:source d:/xxx.sql
说明:查询时,假如有中文要显示,可先执行set names gbk.

1.2.2.创建并配置项目
1.打开IDE并进行配置(新的工作区)
1)配置工作区编码(utf-8)
2)配置maven环境(本地库,私服配置)

2.创建maven项目并添加依赖
项目名称:CGB-MYBATIS-01
打包方式:jar包
组id:com.cy

MySQL驱动依赖(假如驱动版本与当前数据库不一致可能会有问题)


	mysql
    mysql-connector-java
	8.0.17

Mybatis 框架依赖(参考官方 mybatis.org/mybatis-3)


	org.mybatis
	mybatis
	3.5.2

Junit单元测试依赖


	junit
	junit
	4.12

说明:学了spring boot以后还有一种测试方式

3.配置项目

在src/main/resources目录下创建mybatis-configs.xml配置文件如下:




	
  
  
    
      
      
      
        
        
        
        
      
    
  

其模板:参考官方配置实现(Getting start )
1.2.3.项目环境单元测试

在测试包中,创建测试类,测试是否可以获取与数据库的连接

public class TestBase {
/**
 * 借助此对象创建SqlSession(通过此对象
 * 实现与数据库之间的会话)
 */
protected SqlSessionFactory factory;
/**
 * 此方会在@Test注解修饰的方法之前执行,
 * 通常用于做一些初始化操作(方法名自己定义)
 */
@Before
public void init()throws IOException{
	InputStream in=
Resources.getResourceAsStream("mybatis-configs.xml");
	factory=new SqlSessionFactoryBuilder().build(in);
	//系统底层建造者模式构建工厂对象(此对象构建过程相对复杂)
	System.out.println(factory);
}
@Test
public void testSqlSessionConnection(){
	SqlSession session=factory.openSession();
	Connection conn=session.getConnection();
	System.out.println(conn);
}
}

API 创建过程分析:
Mybatis架构_第8张图片
1.3.MYBATIS业务应用快速实践
参考官网:www.mybatis.org/mybatis-3 实现对商品模块数据的CRUD操作。

1.3.1.基于业务实现
添加业务实现(以添加商品信息为例进行实现)
第一步:编写pojo对象(com.cy.pj.goods.pojo.Goods),
借助此对象存储内存中的数据,最后将内存中的数据持久化到数据库.

package com.cy.pj.goods.pojo;
import java.io.Serializable;
import java.util.Date;
/**
 * POJO:借助此对象在内存中封装商品信息
 */
public class Goods implements Serializable{
	private static final long serialVersionUID = 6239917530570596544L;
	private Long id;
	private String name;
	private String remark;
	private Date createdTime;
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getRemark() {
		return remark;
	}
	public void setRemark(String remark) {
		this.remark = remark;
	}
	public Date getCreatedTime() {
		return createdTime;
	}
	public void setCreatedTime(Date createdTime) {
		this.createdTime = createdTime;
	}

}

第二步:编写映射文件(mapper/GoodsMapper.xml),实现内存中对象(一般是pojo对象)与数据库表的映射(例如属性与字段进行映射)




    
        insert into tb_goods
        (id,name,remark,createdTime)
        values
        (#{id},#{name},#{remark},now())
    

第三步:注册映射文件(mybatis-configs.xml),在配置文件中添加如下语句:


 
  

第四步:定义单元测试类(TestGoodsDao)及方法进行单元测试

package com.test;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.cy.pj.goods.pojo.Goods;
public class TestGoodsDao01 extends TestBaseWithXml {
	@Test
	public void testInsertObject() {
		//1.创建Goods对象
		Goods g=new Goods();
		g.setId(14L);
		g.setName("IO");
		g.setRemark("IO...");
		//2.将Goods对象写入到数据库
		//2.1获取SqlSession对象
		SqlSession session=
		sqlSessionFactory.openSession();
		//2.2将对象持久化
		String statement="com.cy.pj.goods.dao.GoodsDao.insertObject";
		int rows=session.insert(statement, g);
		System.out.println("insert.rows=+"+rows);
		//2.3提交事务
		session.commit();
		//2.4释放资源
		session.close();
	}
第二步:修改单元测试类(TestGoodsDao01),添加修改测试方法.	

@Test
public void testUpdateObject() {
	//1.创建Goods对象
	Goods g=new Goods();
	g.setId(14L);
	g.setName("Thread");
	g.setRemark("Thread...");
	//2.将Goods对象写入到数据库
	//2.1获取SqlSession对象
	SqlSession session=
			sqlSessionFactory.openSession();
	//2.2将对象持久化
	String statement="com.cy.pj.goods.dao.GoodsDao.updateObject";
	int rows=session.update(statement, g);
	System.out.println("update.rows=+"+rows);
	//2.3提交事务
	session.commit();
	//2.4释放资源
	session.close();
}
}

1)删除业务实现(以删除作者信息为例进行实现)
step01:修改映射文件(mapper/GoodsMapper.xml),添加delete元素,实现基于id进行删除操作语句的定义.


   delete from tb_goods
   where id=#{id}

step02:修改单元测试类(TestGoodsDao01),添加删除测试方法

@Test
public void testDeleteById() {
	//1.获取session对象
	SqlSession session = 
	sqlSessionFactory.openSession();
	//2.执行删除操作
	String statement=
	"com.cy.pj.goods.dao.GoodsDao.deleteById";
	try {
	int rows=session.delete(statement, 10);
	System.out.println("delete.rows="+rows);
	//3.提交事务
	session.commit();
	//4.释放资源
	}finally {
	session.close();
	}
}

2)查询业务实现(以查询某个商品信息为例进行实现)

step01:修改映射文件(mapper/GoodsMapper.xml),添加select元素,实现基于id查询作者信息语句的定义.


step02:修改单元测试类(TestGoodsDao01),添加基于id执行查询的操作

@Test
public void testSelectById() {
	//1.获取session对象
	SqlSession session = 
			sqlSessionFactory.openSession();
	//2.执行删除操作
	String statement=
			"com.cy.pj.goods.dao.GoodsDao.selectById";
	try {
		Goods g=session.selectOne(statement,11);
		System.out.println(g);
		//3.提交事务
		session.commit();
		//4.释放资源
	}finally {
		session.close();
	}
}

3)分页查询实现(以查询多个商品信息为例进行实现)
step01:修改映射文件(mapper/GoodsMapper.xml),添加select元素,实现基于分页条件查询作者信息语句的定义.


step02:修改单元测试类(TestGoodsDao01),添加基于分页条件执行查询.

@Test
public void testFindPageObjects() { 
		//1.获取session对象
		SqlSession session = 
		sqlSessionFactory.openSession();
		//2.执行删除操作
		String statement=
		"com.cy.pj.goods.dao.GoodsDao.findPageObjects";
		try {
		Map map=new HashMap();
		map.put("startIndex", 0);
		map.put("pageSize", 3);
		List list=
		session.selectList(statement,map); 
		System.out.println(list);
		//3.提交事务
		session.commit();//connection.commint
		//4.释放资源
		}finally {
		session.close();
		}
	}

4)删除多条数据(以删除商品信息为例进行删除,借助动态sql)
step01:修改映射文件(mapper/GoodsMapper.xml),添加delete元素,实现基于多个ID删除作者信息的语句的定义.

   
   delete from tb_goods
    
     
     
          or id=#{id}
     
   

step02:修改单元测试类(TestGoodsDao01),添加基于多个ID执行删除的操作.

@Test
public void testDeleteObjects() { 
	//1.获取session对象
	SqlSession session = 
	sqlSessionFactory.openSession();
	//2.执行删除操作
	String statement=
	"com.cy.pj.goods.dao.GoodsDao.deleteObjects";
	try {
	int rows=session.delete(statement, new Object[] {13,3});
	System.out.println("delete.rows="+rows);
	//3.提交事务
	session.commit();//connection.commint
	//4.释放资源
	}finally {
	session.close();
	}}

1.3.2.基本业务进阶实现

基于接口方式实现数据访问操作
1)分页查询实现(以查询多个作者信息为例进行实现)
第一步: 创建一个接口GoodsDao 定一个分页查询方法

package com.cy.pj.goods.dao;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.cy.pj.goods.pojo.Goods;
public interface GoodsDao {
 /**
    * 执行数据的删除操作
  * @param ids
  * @return
  */
 int deleteObjects(int... ids);
}

第二步: 创建一个单元测试类TestGoodsDao02对分页查询进行单元测试

public class TestGoodsDao02 extends TestBaseWithXml {
@Test
public void testDeleteObjects() { 
	//1.获取session对象
	SqlSession session = 
	sqlSessionFactory.openSession();
	try {
	//2.执行删除操作
	GoodsDao gDao=
	session.getMapper(GoodsDao.class);
	int rows=gDao.deleteObjects(11,12);
	System.out.println("delete.rows="+rows);
	//3.提交事务
	//session.commit();//connection.commint
	//4.释放资源
	}finally {
	session.close();
	}
}
}

2)基于多个id进行删除 (以删除作者信息为例进行实现)
第一步:修改GoodsDao接口,添加deleteObjects方法

 int deleteObjects(int... ids);

第二步: 修改单元测试类,添加testDeleteObjects方法实现单元测试.

@Test
public void testDeleteObjects() { 
	//1.获取session对象
	SqlSession session = 
	sqlSessionFactory.openSession();
	try {
	//2.执行删除操作
	GoodsDao gDao=
	session.getMapper(GoodsDao.class);
	int rows=gDao.deleteObjects(11,12);
	System.out.println("delete.rows="+rows);
	//3.提交事务
	//session.commit();//connection.commint
	//4.释放资源
	}finally {
	session.close();
	}
}

3)基于注解(Annotation)方式做业务实现?
第一步:在GoodsDao接口中添加统计记录行数的方法:getRowCount()

int getRowCount();

第二步:在GoodsDao接口的统计记录方法上添加@Select注解,定义查询语句。

@Select("select count(*) from tb_goods")
int getRowCount();

第三步:在TestGoodsDao02测试类中添加测试方法。

@Test
public void testGetRowCount() {
	SqlSession session=
	sqlSessionFactory.openSession();
	try {
	GoodsDao dao=
	session.getMapper(GoodsDao.class);
	int rowCount=dao.getRowCount();
	System.out.println(rowCount);
	session.commit();
	}finally {
	session.close();
	}
}

1.3.3.会话工厂对象创建增强(了解)
基于非XMl方式构建SqlSessionFactory对象?

package com.test;
import com.cy.pj.goods.dao.GoodsDao;
/**
 * Building SqlSessionFactory without XML
 */
public class TestBaseWithJava {
    protected SqlSessionFactory sqlSessionFactory;
	@Before
	public void init() {
		//1.构建数据源对象
		PooledDataSource dataSource=new PooledDataSource();
		dataSource.setDriver("com.mysql.cj.jdbc.Driver");
	dataSource.setUrl("jdbc:mysql:///dbgoods?serverTimezone=GMT&characterEncoding=utf8");
		dataSource.setUsername("root");
		dataSource.setPassword("root");
		//2.创建事务管理工厂
		JdbcTransactionFactory transactionFactory=
		new JdbcTransactionFactory();
		//3.创建一个环境对象
		Environment env= new Environment("development",
		transactionFactory, dataSource);
		//4.创建配置对象
		Configuration config=
		new Configuration(env);
		config.addMapper(GoodsDao.class);
		//5.创建sqlSessionFactory对象
		sqlSessionFactory=
        new SqlSessionFactoryBuilder()
		.build(config);
	}
	@Test
	public void testConnection(){
		Connection conn=
		sqlSessionFactory.openSession()
		.getConnection();
		System.out.println(conn);
	}
}

测试实现

package com.test;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.cy.pj.goods.dao.GoodsDao;
/**
 * 	基于接口方式进行测试实现
 */
public class TestGoodsDao03 
     extends TestBaseWithJava {
	@Test
	public void testGetRowCount() {
		SqlSession session=
		sqlSessionFactory.openSession();
		try {
		GoodsDao dao=
		session.getMapper(GoodsDao.class);
		int rowCount=dao.getRowCount();
		System.out.println(rowCount);
		session.commit();
		}finally {
		session.close();
		}
	}
}

1.4.MYBATIS 应用原理进阶分析
1.4.1.会话工厂创建分析(了解)
会话工厂(SqlSessionFactory)创建过程分析:基于xml方式
Mybatis架构_第9张图片
1.通过IO读取配置文件
2.解析IO数据并进行封装(所有信息都会存储到Configuration对象)
3.基于配置对象创建SqlSessionFactory对象

会话工厂(SqlSessionFactory)创建过程分析:不基基于xml方式
Mybatis架构_第10张图片
1.4.2.会话对象应用分析 (了解)
说说mybatis对jdbc的封装过程(了解)?(SqlSession应用增强分析)
Mybatis架构_第11张图片
1)封装了连接获取过程?(Executor->JDBCTransactionConnection)
2)封装了Statement对象创建过程?(connection)
3)封装了sql的发送过程,参数的处理过程,结果的映射过程。

1.4.3.基于Mapper接口会话(了解)
例如:XxxMapper xm=session.getMapper(XxxDao.class);
当获取到mapper接口对应的实现类对象以后可以基于实现类底层执行sql操作。
Mybatis架构_第12张图片
FAQ?
1)不同通过getMapper方式获取指定接口实现类是否可以基于mybatis实现数据访问操作?(可以)
2)基于Mapper接口访问数据库库的优势,劣势?(解耦,可读性好但是性能相对较差,相对于直接通过sqlsession的selectList,update方法而言).

1.4.4.缓存应用实现过程分析(掌握)
Mybatis 框架提供一种缓存机制,通过缓存的应用来提高查询性能,但可能会有一定的不一致(脏读)问题。Mybatis中的缓存提供一级缓存和二级缓存的实现,默认都是开启状态(可参考官网)。

1.Mybatis 一级缓存
Mybatis中的一级缓存有时又称为SqlSession级缓存,SqlSession关闭时一级缓存失效。在同一个SqlSession内部多次执行同一个查询,后续的查询会从此缓存取数据.一级缓存结构分析如下:
一级缓存基本架构应用分析:
Mybatis架构_第13张图片
一级缓存代码架构分析:
Mybatis架构_第14张图片
说明:一级缓存的实现可查看BaseExecutor类中的localCache属性。

Mybatis中的二级缓存有时又称跨session缓存,可在多个SqlSession间共享数据,假如要使用二级缓存,可在对应的mapper文件中借助cache元素进行配置(可参考官方映射文件配置),二级缓存架构分析如下:

二级缓存基本应用架构:
Mybatis架构_第15张图片
二级缓存代码结构分析:
Mybatis架构_第16张图片
说明:
1)二级缓存的实现可查看CachingExecutor类中的query方法.
2)二级缓存底层采用装饰模式进行设计,例如:
new SynchronizedCache (new LoggingCache(…));
3)二级缓存中可以存储序列化对象(readWrite属性值为false时)
4)使用二级缓存可在映射文件中借助标签进行声明,也可在接口中借助注解@CacheNamespace对接口进行描述.
5)当映射配置有注解方式,也有xml方式时可通过缓存引用进行配置,例如.
@CacheNamespaceRef(name="com.cy.pj.googs.dao.GoodsDao ")

你可能感兴趣的:(Mybatis架构)