Mybatis设计分析一

Posted 踩踩踩从踩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis设计分析一相关的知识,希望对你有一定的参考价值。

Mybatis进阶功能介绍_踩踩踩从踩的博客-CSDN博客

前言

前面文章主要针对mybatis的进阶的使用有了个大的了解,包括缓存自定义  以及开启二级缓存机制,以及如何达到多数据源,按照我之前的写法通过路由 去解决 不同的数据源的问题,当然可以,有些时候其实也不用这么麻烦 ,毕竟具体的业务 具体分析,roundRobinDataSouceProxy  这个方法就可以 随意去变换,不能固定一个思维;本篇文章会继续介绍 mybatis 的原理,通过分析mybatis的书写的sql来开始分析 整个 mybatis如何进运行,mybatis框架考虑到的是那些点。

Mybatis

一个半自动化的orm 框架( Object Relation Mapping)。    ORM(Object Relational Mapping)框架采用 元数据来描述对象与关系映射的细节,元数据一般采用 XML格式,并且存放在专门的对象一映射文件中。简单理解为一种框架的格式。

这也是 现代 编程 非常喜欢的方式,还是因为对于java编程来说,所有的操作 都采用 对象来获取这样的方式 是非常沉重的,而且代价是非常大。就像下面的方式一样。

@Component
public class UserDao 

	@Autowired
	private DataSource dataSource;

	public void do1(String id, String name) 
		System.out.println(id + name);
	

	public void addUser(User user) throws SQLException 

		try (
				// 1、获取连接
				Connection conn = DataSourceUtils.getConnection(dataSource);
				// 2、创建预编译语句对象
				PreparedStatement pst = conn.prepareStatement(
						"insert into t_user(id,name,sex,age,address,phone,wechat,email,account,password) "
								+ " values(?,?,?,?,?,?,?,?,?,?)");) 
			// 3、设置参数值
			int i = 1;
			pst.setString(i++, user.getId());
			pst.setString(i++, user.getName());
			pst.setString(i++, user.getSex());
			pst.setInt(i++, user.getAge());
			pst.setString(i++, user.getAddress());
			pst.setString(i++, user.getPhone());
			pst.setString(i++, user.getWechat());
			pst.setString(i++, user.getEmail());
			pst.setString(i++, user.getAccount());
			pst.setString(i++, user.getPassword());

			// 4、执行语句
			int changeRows = pst.executeUpdate();
		
	

	public List<User> queryUsers(String likeName, int minAge, int maxAge, String sex) throws SQLException 
		// 1、根据查询条件动态拼接SQL语句
		StringBuffer sql = new StringBuffer(
				"select id,name,sex,age,address,phone,wechat,email,account,password from t_user where 1 = 1 ");
		if (!StringUtils.isEmpty(likeName)) 
			sql.append(" and name like ? ");
		

		if (minAge >= 0) 
			sql.append(" and age >= ? ");
		

		if (maxAge >= 0) 
			sql.append(" and age <= ? ");
		

		if (!StringUtils.isEmpty(sex)) 
			sql.append(" and sex = ? ");
		

		try (Connection conn = DataSourceUtils.getConnection(dataSource);
				PreparedStatement pst = conn.prepareStatement(sql.toString());) 
			// 2 设置查询语句参数值
			int i = 1;
			if (!StringUtils.isEmpty(likeName)) 
				pst.setString(i++, "%" + likeName + "%");
			

			if (minAge >= 0) 
				pst.setInt(i++, minAge);
			

			if (maxAge >= 0) 
				pst.setInt(i++, maxAge);
			

			if (!StringUtils.isEmpty(sex)) 
				pst.setString(i++, sex);
			

			// 3 执行查询
			ResultSet rs = pst.executeQuery();

			// 4、提取结果集
			List<User> list = new ArrayList<>();
			User u;
			while (rs.next()) 
				u = new User();
				list.add(u);
				u.setId(rs.getString("id"));
				u.setName(rs.getString("name"));
				u.setSex(rs.getString("sex"));
				u.setAge(rs.getInt("age"));
				u.setPhone(rs.getString("phone"));
				u.setEmail(rs.getString("email"));
				u.setWechat(rs.getString("wechat"));
				u.setAccount(rs.getString("account"));
				u.setPassword(rs.getString("password"));
			

			rs.close();

			return list;
		
	

采用jdbc的方式 去交互 。这里面 的缺点:

  • 获取连接 不方便 自己封装连接池  
  • 使用方面 不方便 connection 用完了,关闭
  • 事务的处理 ResultSet get结果 Object业务对象  怎么 关系 映射起来
  • 某个表字段 关系型数据库 期望目标结果 Object对象   

半自动  自己定义SQL语句    而会自动 映射到对象中  , 半自动的含义 就是 这点的区别  sql需要自定义  而 映射不用我们自己做。 自动映射  ,这也是  mybatis基于这个思想出来的。  

对于hibernate上的区别。  

现在 为了提高开发效率  有Mybatis-Generator  等工具  为我们的开发  提升效率  这也是找到 我们经常做的事情 而找到的规律,然后 自动 生成工具。 对于mybatis框架 更加 提升了。

灵活的写sql.

Mybatis完成的工作

在面向对象编程中,我们操作的都是对象, Mybatis 框架是一个数据访问层的框架,帮我们完成对象在数据库中的存、取工作。 为什么称为半自动化? 关系型数据库的操作是通过 SQL 语句来完成的, Mybatis 在帮我们做对象的存取时,需要我们提供对应的 SQL 语句,它不自动帮我们生成SQL 语句,而只帮我们完成: 1 对象属性到 SQL 语句参数的自动填充; 2 SQL 语句执行结果集到对象的自动提取; 所以称为半自动的。而我们了解的另一个 ORM 框架 Hibernate 则是全自动的。 半自动化的不足:我们得辛苦一点编写 SQL 语句。 半自动化的优点:我们可以完全把控执行的 SQL 语句,可以随时灵活调整、优化。 为什么要使用mybatis的原因。 1 mybatis 学习、使用简单 2 、半自动化的优点 都是为了提高生产效率,少写代码,少写重复代码! 参数设置代码、结果集处理代码、 JDBC 过程代码都会大量重复,毫无技术含量!

框架确切需求

1 、用户只需定义持久层接口( dao 接口)、接口方法对应的 SQL 语句。 2 、用户需指明接口方法的参数与语句参数的对应关系。 3 、用户需指明查询结果集与对象属性的映射关系。 4 、框架完成接口对象的生成, JDBC 执行过程。 其实 还有个  有缓存 呀  还有 分页呀  上  功能增强  这也是 这个框架上的需求。、 给用户提供的便利。

Mybatis上的设计

用户只需定义持久层接口(dao接口)、接口方法对应的SQL语句。

设计问题:  

 提供什么样的方式来让用户定义SQL语句,

 mybatis开发者肯定 采用注解 和 xml的方式,虽然有点马后炮 ,但是java大家都知道 就采用这两种方式最好

 XML:独立于代码,修改很方便(不需改代码)  注解:直接加在方法上,零xml配置。

SQL语句怎么与接口方法对应 还有 应不应该进行区分 开 增删改查

SQL语句可做增、删、改、查操作,是否要对SQL做个区分

jdbc 中对应有不同的方法 executeQuery executeUpdate xml :设计增删改查的元素:  
<!ELEMENT insert(#PCDATA) > <!ELEMENT update(#PCDATA) > <!ELEMENT delete(#PCDATA) > <!ELEMENT select (#PCDATA) >
注解:设计增删改查的注解: @Insert @Update @Delete @Select ,注解项定义 SQL  并且SQL语句与接口方法对应 为元素定义一个 id id 的值为对应的类名 . 方法名
<insert id = "com.study.mybatis.sample.UserDao.addUser" >      insert into t_user(id,name,sex,age) values(?,?,?,?) </insert>
一个 Dao 接口中可能会定义很多个数据访问方法, id 这么写很长,能不能便捷一点? 这是在做 SQL 与接口方法的映射,我们来加一个 mapper 元素,它可包含多个 insert update delete select 元素,相当于分组,一个接口中定义的分到一组。 mapper 中定义一个属性 namespace ,指定里面元素的名称空间, namespace 的值对应接口类名,里面元素的 id 对应方法名。

这个xml文件命名为 userDaoMapper.xml,内容如下:

<mapper namespace="com.study.mybatis.sample.userDao">
<insert id="addUser">
 insert into t_user(id,name,sex,age) values(?,?,?,?)
  </insert>
</mapper>

 截取最后一个点,根据方法名  进行 。

这些SQL语句、对应关系我们框架需要获取到,谁来获取?又该如何表示存储

xml 方式: 解析 xml 来获取 注解方式: 读取注解信息
@Insert("insert into t_user(id,name,sex,age) values(?,?,?,?)")
	void addUser(User user);

	@Select("select id,name,sex,age,address from t_user where sex = #sex order by #orderColumn")
	List<User> query(String sex, String orderColumn);

	void doSomething();
	
	void do1(String name, String age);
1.怎么表示 得设计一个类来表示从 xml 、注解获得的 SQL 映射信息。

id为唯一id.

xml 方式: id=namespace.id 属性值 注解方式: id= 完整类名 . 方法名

2.怎么存储得到的 MappedStatement   这样的是 有多个 的  怎么样快速获得。 其实就是一个配置信息,我们定义一个 Confifiguration 类:

其实 是将 所有的 mappedstatement都存储起来。

key MappedStatementid  

3 、得有类来负责解析 xml

XmlMapperBuilder 负责解析 xml 文档( parse 方法的 resource 参数用来指定 inputStream 的来源),它调用 XMLStatementBuilder 来解析里面的 是不是感觉很像 spring  框架, 这些框架 其实都很像的。 4.mapper 中可以让用户如何来指定文件位置

 文件可以是在类目录下,也可是在文件系统目录下。

规定: 类目录下的方式通过 resource 属性指定; 文件系统文件通过 url 属性指定,值采用 URL 本地文件格式指定: fifile:///    也就是 我们在 设置 sqlsessionfactory     需要设置 mapperlocation的含义。
<configuration>
<mappers>
<mapper resource="com/mybatis/UserMapper.xml"/>
<mapper url="file:///var/mappers/CourseMapper.xml"/>
 <mappers>
</configuration>

定义 mybatis-confifig.dtd 

<!ELEMENT configuration ( mappers ?)+ > <!ELEMENT mappers ( mapper * )> <!ELEMENT mapper EMPTY > <!ATTLIST mapper resource CDATA #IMPLIED   url CDATA #IMPLIED >
增加了一个 confifig xml 文件,就的有类来解析它。 增加解析 mybatis-confifig.xml 配置文件的类

注解的方式需要获取SQL映射信息,也得有个类来做这件事 

 谁来使用MapperAnnotationBuilder 进行解析 

Confifiguration 吧,在它里面持有 MapperAnnotationBuilder ,增加添加 Mapper 接口类的方法。

用户如何来指定Mapper接口类

mybatis-confifig.xmlmappers中通过mapper指定

<configuration>
<mappers>
<mapper resource="com/mybatis/UserMapper.xml"/>
<mapper url="file:///var/mappers/CourseMapper.xml"/>
 <mappers>
</configuration>
mapper 加一个属性 class 来专门指定 Mapper 类名
<configuration>
<mappers>
<mapper resource="com/mybatis/UserMapper.xml"/>
<mapper url="file:///var/mappers/CourseMapper.xml"/>
<mapper class="com.study.mybatis.dao.UserDao" />
 <mappers>
</configuration>

指定肯定不是 一个个 的指定,对于mybatis肯定是指定包名进行  指定的 

指定一个包名,包含包下所有接口、子孙包下的接口类 mappers 元素中增加一个 package 元素, pacakge 元素定义三个属性 mybatis-confifig.dtd
<!ELEMENT configuration (mappers?)+ >
<!ELEMENT mappers (mapper*,package*)>
<!ELEMENT mapper EMPTY>
<!ATTLIST mapper
resource CDATA #IMPLIED
url CDATA #IMPLIED
class CDATA #IMPLIED
>
<!ELEMENT package EMPTY>
<!ATTLIST package
name CDATA #IMPLIED
type CDATA #IMPLIED
annotation CDATA #IMPLIED
>
<configuration>
<mappers>
<mapper resource="com/mybatis/UserMapper.xml"/>
<mapper url="file:///var/mappers/CourseMapper.xml"/>
<mapper class="com.study.mybatis.dao.UserDao" />
<package name="com.study.mybatis.mapper" />
<package name="com.study.mybatis.mapper"
type="com.study.mybatis.MapperInterface"/> <package
name="com.study.mike.mapper"   annotation="com.study.mybatis.mybtis.annotation.Mapper"/>
<package name="com.study.mybatis.mapper"
type="com.study.mike.MapperInterface" annotation="com.study.mike.mybtis.annotation.Mapper"/>
 <mappers>
</configuration>
为了用户使用方便,我们给定义一个 @Mapper 注解,默认规则:指定包下加了 @Mapper 注解的接口

 扫描就知道了。  这是mybatis给我们提供的方式。

加了 package 元素,又得在 Confifiguration 中增加对应的方法了:  最开始 mybatis采用xml进行 一一对应的,接口 就有具体的实现类。都会有

 约定俗成的规则:指定包下扫到的@Mapper接口,例如UserDao,还可以在包下定义 UserDao.xml,会被加载解析。

用户需指明接口方法的参数与语句参数的对应关系。

 语句参数指定

@Mapper 
public interface UserDao 
 @Insert("insert into t_user(id,name,sex,age) values(?,?,?,?)") 
 void addUser(User user); 

这里 主要是  

User 对象的属性如何与 values(?) 对应? 靠解析 t_user(id,name,sex,age) 可行吗? 如果不添加别名 肯定不知道怎么将值1对1进行  对应起来。 这里需要个名称,交给用户去设置。 # 属性名 代替,我们来解析 SQL 语句中的 # 属性名 来决定参数对应 完全可以要求用户必须与参数名对应 : #xname 。  为了提高点自由度(及后面方便 SQL 复用),可以定义一个注解让用户使用,该注解只可用在参数上

UserOrg中都有id属性,name属性

 

 如果方法参数是对象,则以 参数名.属性.属性 的方式指定SQL参数

 还是得根据名字来区分开  属性名称。

对于map的话还是一样的 key  map.key  都可以一样的。

order by #orderColumn order by 可以吗? 不可以,也就是说 方法参数不全是用来做 SQL 语句的预编译参数值的,有些是来构成 SQL 语句的一部分的。

对于预编译的情况 

一样,定义个 规则: $ 属性名 表示这里是字符串替换都是 可以 

SQL中参数映射解析

SQL 中参数映射解析要完成 的是 解析出真正的 SQL 语句 获得方法参数与语句参数的对应关系 : 问号N--- 哪个参数值 至于怎么 执行 一个mapper接口 SqlSession SqlSessionFactory

以上是关于Mybatis设计分析一的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis架构设计及源代码分析系列:MyBatis架构

《深入理解mybatis原理1》 MyBatis的架构设计以及实例分析

MyBatis的深入原理分析之1-架构设计以及实例分析

MyBatis的架构设计以及实例分析

Mybatis设计与源码分析

Mybatis设计与源码分析