Mybatis设计分析一
Posted 踩踩踩从踩
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis设计分析一相关的知识,希望对你有一定的参考价值。
前言
前面文章主要针对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 为MappedStatement的id
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.xml的mappers中通过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 复用),可以定义一个注解让用户使用,该注解只可用在参数上User和Org中都有id属性,name属性
如果方法参数是对象,则以 参数名.属性.属性 的方式指定SQL参数:
还是得根据名字来区分开 属性名称。
对于map的话还是一样的 key map.key 都可以一样的。
order by #orderColumn order by ? 可以吗? 不可以,也就是说 方法参数不全是用来做 SQL 语句的预编译参数值的,有些是来构成 SQL 语句的一部分的。对于预编译的情况
一样,定义个 规则: $ 属性名 表示这里是字符串替换都是 可以SQL中参数映射解析
SQL 中参数映射解析要完成 的是 解析出真正的 SQL 语句 获得方法参数与语句参数的对应关系 : 问号N--- 哪个参数值 至于怎么 执行 一个mapper接口 SqlSession SqlSessionFactory以上是关于Mybatis设计分析一的主要内容,如果未能解决你的问题,请参考以下文章