目录
一、原生jdbc — jdbc utils工具类 — ORM框架
七、动态sql (if、where、foreach、sql片段)
一、原生jdbc — jdbc utils工具类 — ORM框架
1、原生jdbc
步骤:
- 加载数据库驱动
- 创建并获取数据库链接
- 创建jdbc statement对象
- 设置sql语句
- 设置sql语句中的参数(使用preparedStatement)
- 通过statement执行sql并获取结果
- 对sql执行结果进行解析处理
- 释放资源(resultSet、preparedstatement、connection)
原生jdbc存在的问题
(1)数据库连接,使用时就创建,不使用立即释放,对数据库进行频繁连接开启和关闭,造成数据库资源浪费,影响数据库性能。
设想:使用数据库连接池管理数据库连接。
(2)jdbc将sql语句硬编码到java代码中,如果sql 语句修改,需要重新编译java代码,不利于系统维护。
设想:将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译。
(3)向preparedStatement中设置参数,对占位符号位置和设置参数值,硬编码在java代码中,不利于系统维护。
设想:将sql语句及占位符号和参数全部配置在xml中。
(4)从resutSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,,不利于系统维护。
设想:将查询的结果集,自动映射成java对象。
2、JDBCUtils
3、ORM框架
ORM:将java程序中的对象自动持久化到关系数据库中
(1)JPA:本身是一种ORM规范,不是ORM框架,由各大ORM框架提供实现。
(2)Hibernate:ORM框架,设计灵巧,性能一般,文档丰富。
(3)MyBatis:本是apache的一个开源项目iBatis,提供的持久层框架包括sql maps和DAO,允许开发人员直接编写sql等。
二、Mybatis是什么?
MyBatis是一个持久层的框架,是apache下的顶级项目。
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis,实质上Mybatis对ibatis进行一些改进。
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
Mybatis让程序员将主要精力放在sql上,通过mybatis提供的映射方式,自由灵活生成 (半自动化(逆向工程),大部分需要程序员编写sql) 。
三、Mybatis和Hibernate区别和应用场景
Hibernate:是一个标准ORM框架(对象关系映射), 入门门槛较高,
不需要程序写sql,sql语句自动生成了, 对sql语句进行优化、修改比较困难。
应用场景:适用与需求变化不多的中小型项目,比如:后台管理系统,ERP(企业资源计划)、ORM(对象关系映射)、OA(办公自动化)等
Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。
Mybatis:mybatis是一个不完全的ORM框架(因为也有部分映射功能),学习门槛低,简单易学
虽然程序员自己写sql, mybatis 也可以实现映射(输入映射、输出映射)。
专注的是sql本身,需要程序员自己编写sql语句, sql修改、优化比较方便。
应用场景:适用与需求变化较多的项目,比如:互联网项目。
Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。
Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句,不过mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。
企业进行技术选型(以低成本、高回报作为技术选型的原则),根据项目组的技术力量进行选择。
总之:按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。
四、Mybatis框架原理
流程:
1. mybatis配置:
SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
mapper.xml 文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
2. 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
3. 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
4. mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
5. Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。
mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
6. Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,
Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
7. Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,
Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
通过SqlSessionFactoryBuilder创建会话工厂SqlSessionFactory
通过SqlSessionFactory创建SqlSession,使用单例模式管理sqlSessionFactory(工厂一旦创建,使用一个实例)
SqlSession是一个面向用户(程序员)的接口。 SqlSession中提供了很多操作数据库的方法(发出sql:增、删、改、查)
如:selectOne(返回单个对象)、selectList(返回单个或多个对象)
五、SqlMapConfig.xml
mybatis的全局配置文件SqlMapConfig.xml,配置内容如下(有顺序):
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
1、properties属性
将数据库连接参数单独配置在db.properties中,只需要在SqlMapConfig.xml中加载db.properties的属性值。
在SqlMapConfig.xml中就不需要对数据库连接参数硬编码。
将数据库连接参数只配置在db.properties中的原因:方便对参数进行统一管理,其它xml可以引用该db.properties。
注意: MyBatis 将按照下面的顺序来加载属性:
在 properties 元素体内定义的属性首先被读取。
然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。
最后读取parameterType传递的属性,它会覆盖已读取的同名属性。
建议:不要在properties元素体内添加任何属性值,只将属性值定义在properties文件中。
在properties文件中定义属性名要有一定的特殊性,如:XXXXX.XXXXX.XXXX 如jdbc.username
2、settings全局参数配置
mybatis框架在运行时可以调整一些运行参数。比如:开启二级缓存、开启延迟加载。。
全局参数将会影响mybatis的运行行为。
3、typeAliases(别名)
在mapper.xml中,定义很多的statement,statement需要parameterType指定输入参数的类型、需要resultType指定输出结果的映射类型。
如果在指定类型时输入类型全路径,不方便进行开发,可以针对parameterType或resultType指定的类型定义一些别名,在mapper.xml中通过别名定义,方便开发。
mybatis默认支持别名:
别名 | 映射的类型 |
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
4、typeHandlers(类型处理器)
mybatis中通过typeHandlers完成jdbc类型和java类型的转换。
通常情况下,mybatis提供的类型处理器满足日常需要,不需要自定义.
mybatis支持类型处理器:
类型处理器 | Java类型 | JDBC类型 |
BooleanTypeHandler | Boolean,boolean | 任何兼容的布尔值 |
ByteTypeHandler | Byte,byte | 任何兼容的数字或字节类型 |
ShortTypeHandler | Short,short | 任何兼容的数字或短整型 |
IntegerTypeHandler | Integer,int | 任何兼容的数字和整型 |
LongTypeHandler | Long,long | 任何兼容的数字或长整型 |
FloatTypeHandler | Float,float | 任何兼容的数字或单精度浮点型 |
DoubleTypeHandler | Double,double | 任何兼容的数字或双精度浮点型 |
BigDecimalTypeHandler | BigDecimal | 任何兼容的数字或十进制小数类型 |
StringTypeHandler | String | CHAR和VARCHAR类型 |
ClobTypeHandler | String | CLOB和LONGVARCHAR类型 |
NStringTypeHandler | String | NVARCHAR和NCHAR类型 |
NClobTypeHandler | String | NCLOB类型 |
ByteArrayTypeHandler | byte[] | 任何兼容的字节流类型 |
BlobTypeHandler | byte[] | BLOB和LONGVARBINARY类型 |
DateTypeHandler | Date(java.util) | TIMESTAMP类型 |
DateOnlyTypeHandler | Date(java.util) | DATE类型 |
TimeOnlyTypeHandler | Date(java.util) | TIME类型 |
SqlTimestampTypeHandler | Timestamp(java.sql) | TIMESTAMP类型 |
SqlDateTypeHandler | Date(java.sql) | DATE类型 |
SqlTimeTypeHandler | Time(java.sql) | TIME类型 |
ObjectTypeHandler | 任意 | 其他或未指定类型 |
EnumTypeHandler | Enumeration类型 | VARCHAR-任何兼容的字符串类型,作为代码存储(而不是索引)。 |
5、mappers(映射配置)
法1:通过resource加载单个映射文件
法2:通过mapper接口加载单个mapper
规范:需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在一个目录中。
此规范使用的前提是:使用的是mapper代理方法
按照上边的规范,将mapper.java和mapper.xml放在一个目录 ,且同名。
法3:批量加载mapper (推荐使用)
指定mapper接口的包名,mybatis自动扫描包下边所有mapper接口进行加载
遵循一些规范:需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在一个目录中
上边规范的前提是:使用的是mapper代理方法
六、Mapper.xml
Mapper.xml映射文件中定义了操作数据库的sql,每个sql是一个statement,映射文件是mybatis的核心。
1、#{}与${}
#{}:表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,即向prepareStatement中的预处理语句中设置参数值,自动进行java类型和jdbc类型转换。
#{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
#{}可以有效防止sql注入,在使用时不需要关心参数值的类型,mybatis会自动进行java类型和jdbc类型的转换。#{}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。使用${}不能防止sql注入,但是有时用${}会非常方便。
<!-- 根据名称模糊查询用户信息 -->
<select id="selectUserByName" parameterType="string" resultType="user">
select * from user where username like '%${value}%'
</select>
如果本例子使用#{}则传入的字符串中必须有%号,而%是人为拼接在参数中,显然有点麻烦,如果采用${}在sql中拼接为%的方式则在调用mapper接口传递参数就方便很多。
//如果使用占位符号则必须人为在传参数中加%
List<User> list = userMapper.selectUserByName("%管理员%");
//如果使用${}原始符号则不用人为在参数中加%
List<User>list = userMapper.selectUserByName("管理员");
再比如order by排序,如果将列名通过参数传入sql,根据传的列名进行排序,应该写为:ORDER BY ${columnName},如果使用#{}将无法实现此功能。
2、几种传递
传递简单类型
传递pojo对象
传递pojo包装对象
传递hashmap
3、几种输出
resultType:输出简单类型
resultType可以指定pojo将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名一致方可映射成功。
resultMap:
如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系 ,resultMap实质上还需要将查询结果映射到pojo对象中。
resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。
七、动态sql (if、where、foreach、sql片段)
mybatis核心 对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接、组装。
通过mybatis提供的各种标签方法实现动态拼接sql。
八、延迟加载
resultMap可以实现高级映射(使用association、collection实现一对一、一对多映射),
association、collection具备延迟加载功能。
延迟加载:先从单表查询、需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
需求:如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。
九、缓存
mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。
mybaits提供一级缓存、二级缓存。
一级缓存是SqlSession级别的缓存。 在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。 不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
关系图如下:
1、一级缓存
原理:
第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。
得到用户信息,将用户信息存储到一级缓存中。
如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读(读到另一个事务的未提交更新数据,即读取到了脏数据)。
第二次发起查询用户id为1的用户信息,先去找缓存中找是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。(若没有就去数据库中查)
mybatis默认支持一级缓存,不需要在配置文件去配置。
2、二级缓存
原理:
sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。
sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。
sqlSession3去执行相同 mapper下sql,执行commit提交,清空该 mapper下的二级缓存区域的数据。
二级缓存与一级缓存区别:二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。 UserMapper有一个二级缓存区域(按namespace分) ,其它mapper也有自己的二级缓存区域(按namespace分)。
每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。
开启二级缓存:mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。
注意,楼主要说话啦:
生产中,大部分使用的是独立的缓存服务器,使用第三方缓存组件替代原生二级缓存,原生二级缓存是有些鸡肋的。
注意,楼主又要说话啦:
思想很重要! 框架只是对思想的实现
设计模式很重要! 框架里大量应用了设计模式,后期会再针对性的写篇文章
生产实践很重要!
以上,个人总结,如有错误之处,敬请指出?