您现在的位置是:首页 > 正文

Mybatis设计分析一

2024-04-01 02:08:39阅读 3

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

网站文章

  • 两万常用汉字的拼音+首字母缩写+unicode编码对照表

    最近做项目遇到一项需求,为了隐藏汉字,对医院名称使用首字母代替,对医生名称用拼音代替。查阅了众多资料,比较分散,也不规范,做一个统一,问题包括以下几方面:1. 读出汉字的拼音首字母可通过Excel 公式实现;2. 读出汉字的拼音实现方法,基本需要借助编程,但是目前网上流传最广的版本是比较老旧的版本,不仅编码覆盖不全,而且有错误的mapping。3. 得到2万个常用的汉字并不容易,最...

    2024-04-01 02:07:59
  • Java参数传递

    要点 读完所有的评论以后,问题终于明白了,至少在一个主要问题上产生了混淆。某些评论认为我的节选是错的,因为对象是按引用传递的。 对象确实是按引用传递的;节选与这没有冲突。节选中说所有 参数都是按值 -- 另一个参数 -- 传递的。下面的说法是正确的:在 Java 应用程序中永远不会传递对象,而只传递对象引用。因此是按引用传递对象。但重要的是要区分参数是如何传递的,这才是该节选的意图。Java 应用

    2024-04-01 02:07:52
  • 【模板】用HTML编写邮件正文 | 各大邮箱几乎都会过滤css样式、js脚本等效果,如何用基础HTML编写?

    【模板】用HTML编写邮件正文 | 各大邮箱几乎都会过滤css样式、js脚本等效果,如何用基础HTML编写?

    2024-04-01 02:07:45
  • Javase | 字符编码、转义字符、方法执行过程中的 “内存分配”

    Javase | 字符编码、转义字符、方法执行过程中的 “内存分配”

    字符编码、转义字符、在控制台上输出 “反斜杠字符”、在控制台上输出 “单引号字符”、方法执行过程中的 “内存分配”.....

    2024-04-01 02:07:06
  • python 脚本第一行怎么写

    一、 python脚本第一行的写法:【#!/usr/bin/env python】。该语句告诉操作系统执行该脚本时,首先到env设置里查找python的安装路径,然后调用对应路径下的解释器程序完成操作...

    2024-04-01 02:06:59
  • jszip压缩服务器文件,使用JSZip压缩驻留在服务器上的PDF

    对不起,在这篇文章中缺少链接:这是我在stackoverflow上的第一篇文章,正如错误消息所说,“[我]需要至少10个声望才能发布2个以上的链接。”下载PDF(或任何二进制文件),你可以使用xhr....

    2024-04-01 02:06:52
  • linux mtp 代码,Linux MTP 的故事

    Linux下面有许许多多糟糕的故事,输入法算是其中之一,不过今天我们要讲的是另外一个故事。这个故事本质上和KDE关系不大,不过也算让我纠结了许久了。首先来介绍一下MTP,MTP是Media Trans...

    2024-04-01 02:06:15
  • Docker私房菜————Docker Compose| Compose安装和使用|快速单机编排|

    Docker私房菜————Docker Compose| Compose安装和使用|快速单机编排|

    目录 1.概念1.1 Docker Compose是什么1.2 YAML1.3 yml 文件配置指令参考1.4 Compose命令说明 2.compose的安装和使用2.1 安装compose2.2...

    2024-04-01 02:06:07
  • ruby on rails 撤销取消操作

    撤销操作一个常见的情况是更改控制器的名字,这时你得删除生成的文件。生成控制器时,除了控制器文件本身之外,Rails 还会生成很多其他文件(参见代码清单 3.6)。撤销生成的文件不仅仅要删除控制器文件,还要删除不少辅助文件。rails generate 命令还会自动修改routes.rb 文件,因此我们也想自动撤销这些修改。)在 Rails 中,可以使用rails des

    2024-04-01 02:06:01
  • 解决Powershell无法执行路径中包含空格的脚本文件

    解决Powershell无法执行路径中包含空格的脚本文件

    当然,如果当前已经处于powershell环境中,可以直接使用。这时可以借助powershell中的调用运算符。下面的示例展示了使用调用运算符和不使用的区别。

    2024-04-01 02:05:53