mybatis
mybatis
为什么要引入mybatis,回答这个问题之前不妨先来看看JDBC的缺点:
硬编码 :SQL语句存在Java代码中,不能很好的分离数据库语句和Java语句,造成代码不易维护;
SQL参数固定:SQL语句的参数固定,使得SQL语句不灵活,无法满足多变的场景。
代码重复度高 :大量的重复代码,以 ResultSet为例,每次都需要重复解析结果集;
底层技术 :JDBC属于底层的技术,不支持分布式,缓存等技术,对于复杂场景的应对不好;
ORM 模型:
ORM 模型是数据库的表和简单 Java 对象(Plain Ordinary Java Object,简称 POJO)的映射关系模型,主
要解决数据库数据和 POJO 对象的相互映射。POJO 对象(Plain Ordinary Java Object):简单的 Java 对象,实际就是普通 JavaBeans。
映射配置:配置分为:类与数据库中表的映射;对象与表中记录的映射;类的属性与数据库中表的字段的映射。
常见的ORM模型:
HIBERNATE:一个全自动的ORM模型,Hibernate可以自动生成SQL语句,自动执行。
-
Hibernate是全自动化ORM; Mybatis是半自动化ORM。
-
Hibernate简化dao层,不用考虑SQL语句的编写和结果映射,重点放在业务逻辑上;
Mybatis需要手写SQL语句以及结果映射。 -
Hibernate是一个重量级的框架,内部生成SQL语句,反射操作太多,导致性能下降;
Mybatis是一个轻量级的框架,需要自己写sql语句,有较少的反射操作。
- Hibernate 不方便做SQL优化,遇到较复杂的SQL语句需要绕过框架实现复杂,
对多字段的结构进行部分映射困难;
Mybatis 不仅可以做SQL优化还可以SQL与Java分离,还可以自行编写映射关系,
复杂的SQL语句Mybatis效率更高
Mybatis优势:
- 可以进行更细致的SQL优化;容易掌握
Hibernate优势;
- dao层开发比mybatis简单,mybatis需要维护SQL和结果映射
- hibernate数据库移植性好mybatis 解决tis和hibernate 区别:
mybatis 解决了jdbc 的问题:
1.数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库连接池可解决此问题。
解决:在SqlMapConfig.xml中配置数据连接池,使用连接池管理数据库链接。
2.Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
3.向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。
4.对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象
解析比较方便。
解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。
为什么选择MyBatis:
简单易学 :易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现;
灵活度高 :通过定制 SQL 语句,可以比较灵活的访问数据库,获取数据;
功能完整 :提供了连接管理,缓存支持,线程支持,(分布式)事务管理等功能,还提供了其他 ORM 模型的支持。
项目结构及环境创建:
其中lib为要用到的包;具体导包请参考:导包教程
bean:用来存放实体类JavaBean
dao:层作用具体参考:点击跳转
mapper:用来存放sqlMapper.xml文件。
jdbc.properties:存放数据库的驱动密码用户名等。
mybatis-config.xml文件内容:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--起别名-->
<typeAliases>
<!--给单个起别名-->
<typeAlias type="com.li.Student" alias="a"/>
<!--批量起别名,扫描整个包-->
<package name="com.li.bean"/>
</typeAliases>
<!-- 和spring整合后 environments配置将废除 -->
<enviroments default="development">
<environment id="development">
<!-- 使用jdbc事务管理 -->
<transactionManager type="JDBC" />
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/myschool?serverTimezone=GMT" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</enviroments>
<mappers>
<!--文件路径sqlMapper文件-->
<mapper resource="com/li/mapper/StudentMapper.xml"/>
</mappers>
</configuration>
-
配置文件层级目录
- :标识配置文件的起始,所有配置信息都
存放在这里
- :配置数据库环境信息,注册数据源,配置
数据库事务
- :用来在MyBatis初始化的时候引入映射器
configuration元素:
标识配置文件的起始,所有配置信息都存放在这里
<configuration>
写配置信息,层次顺序不能变!!!
</configuration>
enviroments元素:
配置数据库环境信息,注册数据源,配置数据库事务。
<enviroments default="development">
<environment id="development">
<!-- 使用jdbc事务管理 -->
<transactionManager type="JDBC" />
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/myschool?serverTimezone=GMT" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</enviroments>
default=“development”:默认使用的数据库环境
id=“development”:单个数据库环境信息的标识
transactionManager type=“JDBC” :配置数据库事务 数据库事务:
JDBC:使用 JDBC 的事务管理机制,就是利用 java.sql.Connection 对象完成对事务的交;
MANAGED:使用 MANAGED 的事务管理机制,这种机制 MyBatis 自身不会去实现事务管理,而是让程序的容器(JBOSS,WebLogic)来实现对事务的管理;
自定义:由使用者自定义数据库事务的管理方式,适用于特殊应用。dataSource type=“POOLED”:配置环境信息中数据源连接的信息。
dataSource type=“POOLED”:配置环境信息中数据源连接的信息。
- UNPOOLED:非连接池的数据源
- POOLED:连接池的数据源
- JNDI:JNDI的数据源
- 自定义数据源:其他类型数据源
property :配置环境信息的属性;
思考一个问题:上述写法中的数据库地址,账号密码都是明文的,完全没有安全性可言,这个问题如何解决呢?
答:在外部创建一个jdbc.properties文件内容如下:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/myschool?serverTimezone=GMT
jdbc.username=root
jdbc.password=123456
然后在主配置文件中读取该文件(注意读取文件必须在第一个)、再将value中的值改为#{文件名.元素}:
这样做,在开发中无序知道数据库的信息,只需在最后上线发布时将真正的jdbc.properties替换原来的文件就行。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties"></properties>
<!--起别名-->
<typeAliases>
<!--给单个起别名-->
<typeAlias type="com.li.Student" alias="a"/>
<!--批量起别名,扫描整个包-->
<package name="com.li.bean"/>
</typeAliases>
<!-- 和spring整合后 environments配置将废除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理 -->
<transactionManager type="JDBC" />
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url"
value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<mappers>
<!--文件路径sqlMapper文件-->
<mapper resource="com/li/mapper/StudentMapper.xml"/>
</mappers>
</configuration>
properties元素:
使用时的建议:①不要使用混合方式,以避免管理混乱 。②首选的方式是 Properties 文件方式。③如存在加密或者安全访问的问题,使用第二种方式,为日后统一管理提供方便
typeAliases元素:
Mybatis内部支持的别名
<!-- 起别名-->
<typeAliases>
<!--给单个起别名-->
<typeAlias type="com.li.Student" alias="a"/>
<!--批量起别名,扫描整个包-->
<package name="com.li.bean"/>
</typeAliases>
起别名之前在sqlMapper中使用时代码如下:
<select id="select" resultType="com.li.Student">
select * from student
</select>
使用别名后,下列是使用批量起别名的方法,直接扫描整个包:
<select id="selectAll" resultType="Student">
select * from student
</select>
mappers
用来在MyBatis初始化的时候引入映射器。这个也可以先使用批量起别名的方式然后再使用别名扫描
<mappers>
<mapper resource="com/li/mapper/StudentMapper.xml"/>
</mappers>
sqlMapper.xml:文件
<!-- 头文件 -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- sql语句 -->
<mapper namespace="test1">
<!--全查-->
<select id="selectStuAll" resultType="Student">
select * from student
</select>
<!--按照Sid查询-->
<select id="selectStuBySid" resultType="Student" parameterType="int">
select * from student where sid = #{sid}
</select>
<!--插入字段-->
<insert id="insertStu" parameterType="Student">
insert into student(sname,birthday,ssex,classid) values(#{sname},#{birthday},#{ssex},#{classid})
</insert>
<!--删除字段-->
<delete id="deleteStu" parameterType="int">
delete from student where sid = #{sid}
</delete>
<!--修改字段-->
<update id="updateStu" parameterType="Student">
update student set sname=#{sname},birthday=#{birthday},ssex=#{ssex},classid=#{classid}
where sid = #{sid}
</update>
</mapper>
MyBatis的生命周期:
-
读取MyBatis配置文件:mybatis-config.xml为MyBatis的全局配置文件,配置了MyBatis的运行环境等信息,例如数据库连接信息。
-
加载映射文件。映射文件即SQL映射文件,该文件中配置了操作 数据库的SQL语句,需要在MyBatis配置文件mybatisconfig.xml中加载。mybatis-config.xml文件可以加载多个映射文件,每个文件对应数据库中的一张表。
-
构造会话工厂:通过MyBatis的环境等配置信息构建会话工厂SqlSessionFactory。
-
创建会话对象:由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有方法。
-
Executor****执行器:MyBatis底层定义了一个Executor 接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。
-
MappedStatement 对象:在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。
-
输入参数映射:输入参数类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输入参数映射过程类似于JDBC对preparedStatement对象设置参数的过程。
-
输出结果映射:输出结果类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输出结果映射过程类似于JDBC对结果集的解析过程。
动态代理:
什么是动态代理:
在接口中有方法的返回值定义,参数的定义,方法名,在sqlMapper.xml 中也对应这接口给予了赋值,这时候dao的实现类就显得多余,这是Mybatis可以帮助我们自动产生实现类,并可以调取方法得到结果,这就是Mybatis的mapper动态代理。
动态代理的规范:
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
-
Mapper.xml文件中的namespace与mapper接口的类路径相同。
-
Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
-
Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
-
Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
selectOne和selectList :
动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。
namespace
mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用pojo包装对象或map对象,保证dao的通用性。
select元素:
<select id="findStudentCount" resultType="int">
查询语句
</select>
id:唯一标识,接口中的方法名
parameterType:参数类型
resultType:结果的类型
resultMap:复杂的结果集映射关系
符号 | 小于 | 小于等于 | 大于 | 大于等于 | 和 | 单引号 | 双引号 |
---|---|---|---|---|---|---|---|
原符号 | < | <= | > | >= | & | ’ | " |
替换符号 | < | <= | > | >= | & | ' | " |
映射器:
查询中传递多个参数:
<!--通过javabean创建实体对象-->
<select id="findStudentByClassidAndSex" resultType="Student">
select * from studnt where sid = #{value} and ssex = #{value}
</select>
<!--通过map进行传递-->
<select id="findStudentPage" parameterType="map" resultType="Student">
select * from student limit #{weizhi},#{size}
</select>
<!--参数必须这样写:param1 param2 param3-->
<select id="findStudentBySexPage" resultType="Studnet">
select * from student where ssex = #{param1} limit #{param2} ,#{param3}
</select>
<!--参数必须这样写:arg0 arg1 arg2-->
<select id="findStudentBySexPages" resultType="Student">
select * from student where ssex = #{arg0} limit #{arg1} ,#{arg2}
</select>
insert元素:
作用是将主键直接进行回填,查询出来的结果中Sid为表中的真实值,没有回填的话,查出来就是0
<!--插入字段:主键回填keyProperty;主键的字段;useGeneratedKeys:是否主键回填,默认是false-->
<insert id="insertStu" parameterType="Student" keyProperty="sid" useGeneratedKeys="true">
insert into student(sname,birthday,ssex,classid) values(#{sname},#{birthday},#{ssex},#{classid})
</insert>
id:唯一标识,接口中的方法名
parameterType:参数类型
keyProperty:表示以哪个列作为属性的主键,不能和 keyColumn 同时使用;
keyColumn:指明哪一列是主键,不能和 keyProperty 同时使用;
useGeneratedKeys:使用 JDBC 的 getGeneratedKeys 方式来取有数据库内部生成的主键;
update元素和delete元素:
<update id="updateStu" parameterType="Student">
update student set sname=#{sname},birthday=#{birthday},ssex=#{ssex},classid=#{classid}
where sid = #{sid}
</update>
<delete id="deleteStu" parameterType="int">
delete from student where sid = #{sid}
</delete>
id:唯一标识,接口中的方法名
parameterType:参数类型
resultMap元素:
主角要解决问题:数据库字段与实体类属性不一致问题:
方案一:字段别名,保持跟属性一致
方案二:resultMap
单表可以只写映射不上的
对于主键字段来说可以使用id 也可以使用 result
<resultMap id="resMap" type="SMaster">
<id column="smid" property="smid"/>
<result column="sm_name" property="smname"/>
<result column="smsex" property="smsex"/>
</resultMap>
<select id="findAllSm" resultMap="resMap">
select * from schoolmaster;
</select>
<insert id="insertSMaster" parameterType="SMaster">
insert into schoolmaster(sm_name,smsex) values (#{smname},#{smsex});
</insert>
<insert id="insertSMaster" parameterType="SMaster">
insert into schoolmaster(sm_name smname,smsex) values (#{smname},#{smsex});
</insert>
reeultMap中的属性说明:
property:映射到列结果的字段或者属性,通常是指 POJO 的属性;
column:对应的数据库字段;
javaType:Java 类型,可以是基本数据类型,也可以是类完全限定名;
jdbcType:JDBC 的类型,基本支持所有常用的数据库类型;
多表联查:
级联:级联(cascade),是指多个对象之间的映射关系,建立数据之间的级联关系提高管理效率。
- 一对一:一个对应唯一的身份对象;eg:中国公民和身份证
- 一对多:一个对象对应多个对象;eg:班级和学生
- 多对多:多个对象对应多个对象;eg:公司角色和公司员工
一对一:
首先在实体类中添加级联表的对象:
在Student中添加association属性(多表联查中没有显示映射的字段不会赋值):
一对多:
首先在实体类中添加级联表的对象list集合:
sqlMapper.xml:
ofType:使用该属性指定集合中的元素的。
Mybatis中不允许方法重载,因为sqlMapper文件中的id是唯一标识,不能重复