学习完了雷丰阳老师视频教程的Mybatis,记录一下。
有些自己的体会,在对不同资源进行整合的时候对于普通java工程项目需要自行添加对应jar包文件到路径下,然后build path,如果是maven工程只需要写依赖就会自动导入。
MyBatis提供了非常丰富的对Sql进行自定义和调整的标签及其属性,很多东西我们只需要在xml文件中定义或者添加注解,MyBatis就会自动完成其他工作,框架开发应该也是这样的发展历程,需要手写的代码会越来越少。
mybatis基础
即ibatis3.0(源于Apache)之后的版本
三层架构:
表现层—展示数据
业务层—处理业务需求
持久层—和数据库交互
-持久层技术解决方案
JDBC技术(最底层):
Connection、PreparedStatement、ResultSet
Spring的JdbcTemplate:
Spring中对jdbc的简单封装
Apache的DBUtils:
和Spring的JdbcTemplate类似
以上都不是框架,jdbc是规范,剩下两个是工具包
-封装不够细致,需要关注加载驱动、创建连接、创建Statement等繁杂过程。
-功能简单,sql语句编写在java代码里,硬编码高耦合(sql变化时需要修改源码)
框架:整体解决方案
Hibernate:全自动全映射ORM框架,旨在消除sql。但是存在开发人员无法自主优化sql语句的缺点。
mybatis是优秀的基于java持久层的框架(半自动),它内部封装了jdbc,sql与java编码分离(sql单独提取到配置文件中,由开发者控制),开发者只需要关注sql语句本身(剩下的预编译、设置参数、执行sql、封装结果由框架完成)。它使用了ORM的思想实现了结果集的封装。
ORM:Object Relational Mapping 对象关系映射
将数据库表和实体类、实体类的属性对应起来,让我们可以实现操作实体类即操作数据表 mybatis通过xml或注解的方式将要执行的statement(sql语句)配置起来,通过java对象 和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行 语句并将结果映射为java对象返回。
mybatis实现总结
1.全局配置文件mybatis-config.xml
包含数据源、事务管理器、运行环境的信息
2.sql映射文件EmployeeMapper.xml -->关键文件,mybatis从该文件抽取出sql
配置每个sql,以及sql的封装规则(查出相应记录后如何封装)
3.将sql映射文件注册在全局配置文件中
4.写代码(MybatisTest.java)
关键:
1)根据配置文件获得SqlSessionFactory
2)使用SqlSessionFactory获取到SqlSession对象,使用该SqlSession对象中的方法进行增删查改
一个SqlSession就是和数据库的一次会话,使用完要关闭
使用sql的唯一标识(如selectOne方法的第一个参数)来告诉mybatis执行哪个sql
接口式编程(方便开发扩展)实现总结
1.创建一个接口(EmployeeMapper.java),其中定义抽象方法
2.将EmployeeMapper.xml中的namespace改为接口的全类名
3.将标签中的"id"改为与接口中定义的抽象方法相同的方法名
4.第2、3步实现了动态绑定,在标签中写方法的具体实现
5.在测试方法中不需要创建接口的实现类,mybatis会为接口自动创建一个代理对象去执行增删改查方法
在mybatis-config.xml和EmployeeMapper.xml文件的头部标签中xxx.dtd文件是约束,规定了xml文件编写规则,联网状态下会
自动加载,按Alt+/会有编写提示
遇到的问题
1.jUnit测试始终无法找到EmployeeMapper.xml映射文件
解决:config文件夹右键->Build Path->Use as Source Folder
2.测试运行MybatisTest的test方法时报错Client does not support authentication protocol
解决:更换mysql-connector-java-xxx.jar文件的版本
全局配置文件Config.xml
将数据库的连接信息提取到dbconfig.properties文件中,放在类路径config文件夹下
然后将全局配置文件中的
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="123456" />
修改为
<property name="driver" value="${jdbc.driver}" />
<property name="url"
value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
运行测试方法,一样成功。
项目庞大时更方便维护管理配置信息。
作为了解学习,使用spring整合后spring会帮我们完成配置。
properties设置
mybatis可以使用properties标签来引入外部properties配置文件的内容。
settings设置
极为重要的调整设置,它们会改变Mybatis运行时行为。
typeAliases设置
别名处理器,可自定义java类型别名。
包含单个处理和批量处理。
批量处理可能会出现别名冲突问题(包下的类和包下的包中的类名字相同),可以使用注解@Alias("")为某个类型指定新的别名。
不使用别名,使用类型的全类名,查找类时更方便。
typeHandles:类型处理器,架起java类型和数据库类型映射的桥梁。(互相转换,String<–>CHAR/VARCHAR)
plugins设置
插件,拦截对象方法。Executor、ParameterHandler、ResultSetHandler、StatementHandler。
environments设置
environments:环境,mybatis可以配置多种环境,开发时可以定义标签的default不同值快速切换环境。
databaseIdProvider设置
用来支持多数据库厂商,在全局配置文件中添加后为各数据库起别名,在mapper.xml中标签中添加databaseId属性,即可指定该sql语句适用的数据库。
然后修改的default值可以切换运行环境
mappers设置
mappers:将写好的sql映射文件(EmployeeMapper.xml)注册到全局配置文件中
标签编写顺序
在全局配置文件中的标签有严格顺序要求:
Content Model : (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?,
objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)
映射文件Mapper.xml
映射文件是mybatis的重中之重。
The true power of MyBatis is in the Mapped Statements. This is where the magic happens.
Mybatis支持增删改返回Integer、Long、Boolean类型返回值,在接口中直接在方法上中定义即可,Mybatis会自动封装
Mybatis参数处理
单个参数
mybatis不会做特殊处理,只有一个参数,即使参数名与bean中属性不一致,运行效果也一样。
#{参数名}:取出参数值。
多个参数
mybatis会做特殊处理,返回的多个参数会被封装成一个map。key是param1…paramN/参数的索引(0,1…)
value才是传入的参数值
#{}就是从map中获取指定key的值
取值:#{param1},#{param2}
多个参数建议使用命名参数,即在封装参数为map时明确指定map的key。
public Employee getEmpByIdAndLastName(@Param(“id”)Integer id, @Param(“lastName”)String lastName);
此时#{指定的key}即可取出对应的参数值
POJO
(简单java对象,简单JavaBean)
如果多个参数正好是业务逻辑的数据模型(传入的参数是Employee的属性值),我们可以直接传入pojo;
#{属性名}:取出传入的pojo的属性值
Map
如果不是业务逻辑的数据模型,没有对应的pojo,不经常使用,为了方便也可以直接传入map
#{key}:取出map中对应的值
TO
(DTO,可以理解为写一个新的、继承原来model层实体类、添加新的业务处理方法、专注于数据传输的类)
如果不是业务逻辑的数据模型,但是该方法要经常使用,推荐来编写一个TO(Transfer Object)即数据传输对象
Page{
int index;
int size;
}
表现层与应用层之间是通过数据传输对象(DTO)进行交互的,数据传输对象是没有行为的POCO对象,它 的目的只是为了对领域对象进行数据封装,实现层与层之间的数据传递。为何不能直接将领域对象用于 数据传递?因为领域对象更注重领域,而DTO更注重数据。不仅如此,由于“富领域模型”的特点,这样 做会直接将领域对象的行为暴露给表现层。
需要了解的是,数据传输对象DTO本身并不是业务对象。数据传输对象是根据UI的需求进行设计的,而不 是根据领域对象进行设计的。比如,Customer领域对象可能会包含一些诸如FirstName, LastName, Email, Address等信息。但如果UI上不打算显示Address的信息,那么CustomerDTO中也无需包含这个 Address的数据。
参数处理扩展思考
一些应用场景
public Employee getEmp(@Param("id")Integer id, String lastName);
取值:id==>#{id/param1} lastName==>#{param2}
public Employee getEmp(Integer id, @Param("e")Employee emp);
取值:id==>#{param1} lastName==>#{param2.lastName/e.lastName}
特别注意
如果传入的是Collection(List、Set)类型或者是数组,也会特殊处理。
也是把传入的list或者数组封装在map中;
key:Collection=>collection,List=>list,Array=>array
public Employee getEmpById(List<Integer> ids);
取值:取出第一个id的值==>#{list[0]}
参数值的获取
#{}:可以获取map中的值或者pojo对象属性的值
${}:也可以获取map中的值或者pojo对象属性的值
#{}和${}的区别
select id, last_name lastName, email, gender from tbl_employee
运行后:
Preparing: select id, last_name lastName, email, gender from tbl_employee where id = 5 and last_name = ?
where id = ${id} and last_name = #{lastName}
#{}是以预编译形式将参数设置到sql语句中;PreparedStatement:防止sql注入
${}是将取出的值直接封装在sql语句中;会有安全问题
大多情况下使用#{}获取参数的值
原生jdbc不支持占位符的地方就可以使用${}进行取值(即sql语句不支持?的地方)
如:select * from ${year}_salary where xxx;
select * from tbl_employee order by ${f_name} ${order}
#{}更丰富的用法:
取值的时候可以规定参数的一些规则
javaType、jdbcType、mode(存储过程)、numbericScale、resultMap、typeHandler、jdbcTypeName、expression(未来准备支持的功能)
jdbcType在某种特定场景下需要被设置,在有的数据为null的时候,有的数据库无法识别mybatis对null的默认处理。比如Oracle会报错,因为mybatis对所有的null都映射的是原生jdbc的OTHER类型,Oracle不兼容该类型。
由于全局配置中,jdbcTypeForNull=OTHER,Oracle不支持。两种解决办法:
1.修改为#{email, jdbcType=NULL},表示为空时设置为NULL类型,运行成功。
2.修改全局配置中jdbcTypeForNull=NULL。
总结
参数多时会封装map,为了不混乱可以使用注解@Param(“xxx”)来指定封装时哪个属性是key
#{xxx}就可以取出map中指定的值
查询相关
1.查询返回List集合
public List<Employee> getEmpsByLastNameLike(String lastName);
resultType:如果返回的是一个集合,要写集合中元素的类型
<select id="getEmpsByLastNameLike" resultType="com.bean.Employee">
select * from tbl_employee where last_name like #{
lastName}
</select>
2.查询返回Map集合
返回一条记录的Map
key就是列名,value就是对应的值
public Map<String, Object> getEmpByIdReturnMap(Integer id);
<select id="getEmpByIdReturnMap" resultType="map">
select * from tbl_employee where id = #{
id}
</select>
返回多条记录的Map
返回多条记录封装成一个map,key是这条记录的主键,value是记录封装后的JavaBean
@MapKey("id")//用注解告诉mybatis封装这个map的时候用哪个属性值作为主键
public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName);
<select id="getEmpByLastNameLikeReturnMap" resultType="com.bean.Employee">
select * from tbl_employee where last_name like #{
lastName}
</select>
3.resultMap自定义javaBean的封装规则
Mapper.xml文件中的select标签中resultType和resultMap只能二选一
<!-- 自定义某个javaBean的封装规则
type:自定义规则的java类型
id:唯一id方便引用
-->
<resultMap type="com.bean.Employee" id="MyEmp">
<!-- 指定主键列的封装规则
用id定义主键会有底层优化,也可以使用result
column:指定哪一列
property:指定对应的javaBean属性
-->
<id column="id" property="id"