原始JDBC操作
对于原始JDBC操作数据库,一般需要经历以下几个步骤 :
- 1.获取数据库资源
- 2.加载驱动
- 3.获取连接
- 4.执行SQL语句
- 5.获取结果集
- 6.归还资源
这些步骤较为繁琐,使得代码冗余,效率较低。
于是,设想是否有一门技术可以是我们只关注业务sql的操作,而这些获取、连接、归还的操作不再关心,而MyBatis框架技术正好可以为我们解决这个问题。
MyBatis框架介绍
- 前身iBatis -Apache的开源项目
- 半自动化的ORM实现
- DAO层
- 动态SQL
- 小巧灵活,简单易学
- 持久化操作
注意:
ORM即:O:对象 R:关系 M:映射
持久化:程序数据的在瞬时状态和持久化状态之间的转换。
特点
- 基于SQL语法、简单易学
- SQL语句封装在配置文件中,便于统一管理与维护,降低程序的耦合度
- 方便程序代码调试
开发步骤
- 导入MyBatis.jar包
- 编写MyBatis核心配置文件
- 创建实体类pojo
- DAO层-SQL映射文件
- 创建测试类
读取全局配置文件MyBatis-config.xml
创建SqlSessionFactory对象 读取配置文件
创建SqlSession对象
调用mapper文件进行数据操作
以上便是MyBatis的简单介绍,其中所提到的ORM简单理解就是:在编写程序时,以面向对象方式处理数据,而保存数据时,以关系型数据库的方式存储。ORM中所涉及的映射指的就是POJO类中的属性名和属性类型必须和数据库中对应的表的字段名和字段类型一致(字段类型可以不一致,但需要通过自定义关系处理器转换,原则上一致比较好)
核心配置文件
MyBatis的核心配置文件主要为以下两个:MyBatisConfig.xml和XXMapper.xml。对于这2个文件的命名一般没有要求,但真正的项目中,配置文件很多,命名最好能明显的知道是什么配置文件。
MyBatisConfig.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">
该头部信息是必不可少的,主要就是一个核心配置文件的头部信息,其中PUBLIC “-//mybatis.org//DTD Config 3.0//EN”
“http://mybatis.org/dtd/mybatis-3-config.dtd” 就是一个dtd主要用于校验你在xml中编写的配置信息是否正确。
配置jdbc和引入XXMapper.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>
<properties resource="database.properties"></properties>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<!-- <typeAlias type="" alias=""/> -->
<package name="com.smbms.pojo"/>
</typeAliases>
<environments default="test">
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/smbms/dao/BillMapper.xml"/>
</mappers>
</configuration>
原始jdbc操作需要使用java代码获取相应的资源,执行相关操作。如今我们将其中一些繁琐的操作交给MyBatis去操作。但是我们需要告诉MyBatis ,我们使用的是什么数据源,以及相关数据源配置信息。
读取数据源相关配置
<properties resource="database.properties"></properties>
database.properties文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/smbms?characterEncoding=UTF-8&serverTimezone=GMT%2B8
username=root
password=1126
此处我将数据源放置在了外部文件中,所以需要通过resource引入,当然也可在properties中直接配置。
注意:如果使用了resource,又在properties标签中直接配置了数据源信息,则外部resource的优先级更高。
MyBatis中可以配置多套数据源-environments,但是需要指定使用哪一套数据源-environment的id。
引入XXMapper.xml
<mappers>
<mapper resource="com/smbms/dao/BillMapper.xml"/>
</mappers>
mapeer引入可以通过resource、class、url引入,其次上述引入方式并不是很友好,如果有多个mapper,则需要多次引入。
一次性引入-name为Mapper文件所在的包名
<mappers>
<package name="com.smbms.dao"/>
<!-- <mapper resource="com/smbms/dao/BillMapper.xml"/> -->
</mappers>
在上述文件中,还涉及到了settings、typeAlias
es、transactionManager、dataSource等配置。其中settings主要用于设置日志信息配置或缓存、映射匹配设置等。typeAliases主要用于给POJO中的类起别名(如果不设置 在Mapper.xml中就需要写完整的限定名)。其中设置方式主要分为以下2种:
<typeAliases>
<!-- <typeAlias type="" alias=""/> -->
<package name="com.smbms.pojo"/>
</typeAliases>
typeAlias 则指定别名,package 引入对象类所在的包名,则别名为对象类的类名。
transactionManager主要事务管理,这里默认事务设置为JDBC,
XXMapper.xml配置文件详情
其配置文件主要是用来配置sql语句即sql映射。
头部
<?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映射
<?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">
<mapper namespace="com.mb.mapper.BillMapper">
<resultMap type="Bill" id="Bills">
<id property="id" column="id"/>
<result property="productName" column="productName"/>
<result property="proName" column="proName"/>
<result property="totalPrice" column="totalPrice"/>
<result property="isPayment" column="isPayment"/>
<result property="createdBy" column="createdBy"/>
</resultMap>
<resultMap type="Bill" id="bil">
<id property="id" column="id"/>
<result property="productName" column="productName"/>
<result property="totalPrice" column="totalPrice"/>
<result property="isPayment" column="isPayment"/>
<association property="provider" javaType="Provider">
<result property="proCode" column="proCode"/>
<result property="proName" column="proName"/>
<result property="proContact" column="proContact"/>
<result property="proPhone" column="proPhone"/>
</association>
</resultMap>
<select id="selectAll" resultMap="Bills" parameterType="Bill">
select sb.id ,sb.productName ,sp.proName ,sb.totalPrice ,sb.isPayment ,sb.createdBy from smbms_bill sb ,smbms_provider sp
where sb.providerId =sp.id and sb.productName like #{productName} and sb.providerId =#{providerId} and sb.isPayment =#{isPayment}
</select>
<insert id="inserts" parameterType="Bill">
insert into smbms_bill(billCode,productName,productDesc,productUnit,productCount,totalPrice,isPayment,createdBy,creationDate,providerId)
values(#{billCode},#{productName},#{productDesc},#{productUnit},#{productCount},#{totalPrice},#{isPayment},#{createdBy},#{creationDate},#{providerId})
</insert>
<select id="selects" resultMap="bil">
select sb.id ,sb.productName ,sp.proCode ,sp.proName ,sp.proContact ,sp.proPhone ,sb.totalPrice ,sb.isPayment from smbms_bill sb ,smbms_provider sp
where sb.providerId =sp.id and productName like #{productName} and isPayment=#{isPayment} and providerId=#{providerId}
</select>
</mapper>
该配置文件较为简单,其中需要注意的地方便是mapper标签的name属性 name为命名空间-唯一性。name可自定义,也可指定一个dao层的接口。如果是自定义名称,则在测试类中调用时需要通过完整的name的名称.select的id(此处以查询为主)调用。如果name设置为对应的dao层接口,这sql的增删改查的id都必须为接口中相应的方法名称。在测试类中调用时候只需要通过发射获取接口中的方法即可。
resultMap标签主要用于自定义映射关系,association 用于处理一对一的映射,collection用于处理一对多和多对多的关系。
测试类
private Logger logger=Logger.getLogger(this.getClass());
@Test
void test() {
try {
InputStream input=Resources.getResourceAsStream("mybatisConfig.xml");
SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(input);
SqlSession session=sqlSessionFactory.openSession();
List<Bill> list=session.getMapper(BillMapper.class).selectBill();
for(Bill bill:list) {
logger.debug(bill.getBillCode()+"\t"+bill.getProductName());
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
通过上述的步骤,可以发现获取sql和执行sql操作主要分为以下三个步骤:
- 读取核心配置文件
- 创建SqlSessionFactoryBuilder
- 通过SqlSessionFactoryBuilder.build()方法创建sqlSessionFactory
- 通过sqlSessionFactory.openSession()方法获取sqlsession
- 通过sqlsession方法反射获取接口中的方法。
所以总上所述就是以下过程:
SqlSessionFactoryBuilder->sqlSessionFactory->sqlsession
三者之间的关系
生命周期
SqlSessionFactoryBuilder
- 用过即丢,其生命周期只存在于方法体内
- 可重用其来创建多个SqlSessionFactory实例
- 负责构建SqlSessionFactory,并提供多个build方法的重载-字节流、字符流、类。
SqlSessionFactory
- 是每个MyBatis应用的核心
- 创建SqlSession的实例
- 作用域:Application
- 生命周期与应用的生命周期相同
- 单例:存在于整个应用运行时,并且同时只存在于一个对象实例
SqlSessionFactory为单例模式,因此专门使用一个工具类再次封装。
public class MyBatisUtils {
private static SqlSessionFactory sessionFactory;
static {
InputStream input=null;
try {
input=Resources.getResourceAsStream("mybatis-config.xml");
sessionFactory =new SqlSessionFactoryBuilder().build(input);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
input.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static SqlSession opneSession() {
return sessionFactory.openSession();
}
public static void CloseSession(SqlSession session) {
session.close();
}
}
SqlSession
- 包含了执行SQL所需的所有方法
- 对应一次数据库会话,会话结束必须关闭
- 线程级别,不能共享,线程不安全
使用方式
- 通过SqlSession实例直接运行映射的SQL语句
- 通过Mapper接口方式操作数据
注意
在SqlSession里可以执行多次SQL语句,但一旦关闭了SqlSession就需要重新创建
sessionFactory.openSession()
该方法可传入参数false或true,默认为false即关闭了事务的自动提交方式,所以当执行增删改操作时需要手动提交事务。
MyBatis核心执行流程
1.MyBatis配置文件
config.xml:配置了全局配置文件,配置了MyBatis的运行环境等信息。
mapper.xml:sql的映射文件,配置了操作数据库的sql语句,此文件需在config.xml中加载。
2.SqlSessionFactory
通过MyBatis环境等配置信息构造SqlSessionFactory(会话工厂)。
3.SqlSession
通过会话工厂创建SqlSession(会话),对数据库进行增删改查操作。
4.Exector执行器
MyBatis底层自定义了Exector执行器接口来具体操作数据库,Exector接口有两个实现,一个基本执行器(默认),一个是缓存执行器,SqlSession底层是通过Exector接口操作数据库。
5.MappedStatement
MyBatis的一个底层封装对象,它包装了MyBatis配置信息与sql映射信息等。mapper.xml中的insert/select/update/delete标签对应一个MappedStatement对象。标签的id就是MappedStatement的id。
MappedStatement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo、Executor通过MappedStatement在执行sql前将输入的Java对象映射至sql中,输入参数映射就是JDBC编程对preparedStatement设置参数。
MappedStatement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过MappedStatement在执行sql后将输出结果映射至Java对象中,输出结果映射就是JDBC编程对结果的解析处理过程。
动态SQL
介绍
MyBatis的强大之处就是动态SQL的拼接,它可以非常灵活的处理SQL拼接。
常用标签
- if
- trim
- where
- set
- choose
- foreach
where
简化SQL语句中where条件判断
智能处理and和or
trim
属性:
- prefix:前缀
- suffix:后缀
- prefixOverrides:首部覆盖或忽略
- suffixOverrides:尾部覆盖或忽略
更灵活的去除多余关键字
替代where
foreach
- 迭代一个集合,通常用于in条件
- 属性
- item
- index
- collection:必须指定-参数类型
- list
- array
- map-key
- open
- separator
- close
choose
- 相当于java中的switch语句
- 满足when有条件满足时就会跳出
MyBatis接收的参数类型
- 基本类型
- 数组
- 对象
- List
- Map
注意:
无论何种参数入参,MyBatis都会将其放入一个Map中
MyBatis参数传递的方式
在执行数据库操作时,往往都是复杂的SQL的增删改查并且是多条件。那如何传递参数呢?其实MyBatis也为我们提供了两种方式:$和#
${}和#{}的详情
区别
相同点:
都能获取到变量的值
不同点
- #{}可以实现预编译,会把#{变量}
提前解析编译成?在执行时再取值。 - ${}直接进行字符串的替换。
详情
${}
$符号主要用于传入的参数是sql片段的场景下,会直接进行字符串替换,完成了sql的拼接
<select id="getUserPage" resultType="com.ymxx.oa.bean.User">
${sql片段}
</select>
适用场景
比如有这样一个需求:对于后台管理系统的table列,想让用户选择展示哪些列,不展示哪些列,或者根据权限来分配不同的用户可以看到哪些列?
存在的问题-SQL注入
因为${}符合是直接进行字符串的替换,所以必然会存在SQL注入问题,可能会导致数据泄露或丢失。
假如这个sql:select * from ${tableName} where name = ${name}
如果tableName的值为 user; delete user; --,该sql最终解析成select * from user; delete user; -- where name = xxx,结果查询了整张表,然后把user表给删了...
#{}
#{}会执行预编译过程,主要体现在数据类型检查和安全检查两方面
数据类型检查表现在:若检测到为数值类型,就不加引号,即?;若检测到位字符串类型,就加上引号,即'?'。
安全检查表现在:若变量的值带有引号,会对引号进行转义处理,这样可以防止sql注入。
应用场景
需要在sql映射文件中动态拼接sql时的开发场景,比如传入多个变量进行条件查询、传入一个POJO进行数据插入等。
MyBatis注解开发
在真时项目中,很有可能会使用到注解开发,因此了解MyBatis基本的注解开发还是很有必要的。
基本CRUD注解
- @Insert:实现新增
- @Update:实现更新
- @Delete:实现删除
- @select:实现查询
- @Result:实现结果集的封装
- @Results:可以与@Result一起使用,封装多个结果集
- @One:实现一对一结果集封装
- @Many:实现一对多结果集封装
简单注解实现
@Select("select * from smbms_bill")
List<Bill> selectAll();
@Delete("delete from smbms_bill where id=#{id}")
int deleteByid(int id);
复杂查询-注解
案例
@Select("select * from smbms_bill")
@Results(
{
@Result(column="id",property = "id"),
@Result(column="billCode",property = "billCode"),
@Result(column="productName",property = "productName"),
@Result(column="productDesc",property = "productDesc"),
@Result(column="productUnit",property = "productUnit"),
@Result(column="productCount",property = "productCount"),
@Result(column="totalPrice",property = "totalPrice"),
@Result(column="isPayment",property = "isPayment"),
@Result(column="createdBy",property = "createdBy"),
@Result(column="creationDate",property = "creationDate"),
@Result(column="modifyBy",property = "modifyBy"),
@Result(column="modifyDate",property = "modifyDate"),
@Result(
property = "provider",//封装的属性名
column = "providerId",//根据哪个字段去查询provider表的数据
javaType = Provider.class,//要封装的实体类
//select属性:代表查询哪个接口的方法获得数据 one:一对一
one=@One(select="com.smbms.dao.Provider.select")
)
}
)
List<Bill> selectPro();
以上便是小编对MyBatis的基本了解&看法,如有错误,请谅解并指出,谢谢!