总体介绍:
MyBatis实际上是Ibatis3.0版本以后的持久化层框架【也就是和数据库打交道的框架】!
和数据库打交道的技术有:
原生的JDBC技术---》Spring的JdbcTemplate技术
这些工具都是提供简单的SQL语句的执行,但是和我们这里学的MyBatis框架还有些不同,框架
是一整套的东西,例如事务控制,查询缓存,字段映射等等。
我们用原生JDBC操作数据库的时候都会经过:
编写sql---->预编译---->设置参数----->执行sql------->封装结果
我们之所以不使用原生的JDBC工具,是因为这些工具:
1.功能简单,sql语句编写在java代码里面【一旦修改sql,就需要将java及sql都要重新编译!】这属于硬编码高耦合的方式。
2.我们希望有开发人员自己编写SQL语句,
并且希望SQL语句与java代码分离,
将SQL语句编写在xml配置文件中,
实现数据表中记录到对象之间的映射!
sql和java编码分开,功能边界清晰,一个专注于业务,一个专注于数据,可以使用简单的
XML或注解用于配置和原始映射,将接口和Java的POJO映射成数据库中的记录,完成
业务+底层数据库的媒介!
1.MyBatis历史
原是Apache的一个开源项目iBatis, 2010年6月这 个项目由Apache Software Foundation 迁移
到了 Google Code,随着开发团队转投Google Code 旗下, iBatis3.x正式更名为MyBatis ,代码于 2013年11月迁移到Github(下载地址见后)。
iBatis一词来源于“internet”和“abatis”的组合,是 一个基于Java的持久层框架。
iBatis提供的持久 层框架包括SQL Maps和Data Access Objects、(DAO)
2.MyBatis简介
MyBatis 是支持定制化 SQL、存储过程以及高级 映射的优秀的持久层框架。 MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
MyBatis可以使用简单的XML或注解用于配置和原 始映射,将接口和
Java的POJO(Plain Old JavaObjects,普通的Java对象)映射成数据库中的记录.
3.为什么要使用MyBatis?
MyBatis是一个半自动化的轻量级的持久化层框架。
JDBC
– SQL夹在Java代码块里,耦合度高导致硬编码内伤
– 维护不易且实际开发需求中sql是有变化,频繁修改的情况多见
Hibernate和JPA
– 长难复杂SQL,对于Hibernate而言处理也不容易
– 内部自动生产的SQL,不容易做特殊优化。
– 基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难。 导致数据库性能下降。
对开发人员而言,核心sql还是需要自己优化
sql和java编码分开,功能边界清晰,一个专注业务、 一个专注数据。
4.去哪里找MyBatis?
https://github.com/mybatis/mybatis-3/
或者在百度直接搜索mybatis,然后找到github下的地址下载即可!
5.为了快速写一个MyBatis的HelloWorld,我们先将数据表建立起来
1.创建数据库及数据表
CREATE DATABASE mytabis;
CREATE TABLE tbl_employee(
id INT(11) PRIMARY KEY AUTO_INCREMENT,
last_name VARCHAR(255),
gender CHAR(1),
email VARCHAR(255)
)
然后插入两条数据;
2.创建一个动态WEB工程,然后创建与上述数据表对应的实体类:
3.[参考mybatis官方文档]加入需要的jar包[mybatis所需要的jar包,和数据库打交道的jar包,以及看打印日志所需要的log4j的jar包]:
log4j-1.2.17.jar //当然需要注意的是:log4j的jar包是需要log4j.xml文件的
mybatis-3.4.1.jar
mysql-connector-java-5.1.37-bin.jar
4.创建mytabis-config.xml文件并将mybatis文档中的内容复制过来,并将数据库配置信息换成自己的:
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mytabis"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 将我们写好的sql映射文件一定要注册到全局配置文件中 -->
<mappers>
<mapper resource="EmployeeMapper.xml"/>
</mappers>
</configuration>
5.创建测试用例,.复制mybatis官方文档代码,代码如下:
public class MyBatisTest {
@Test
public void test() throws IOException {
String resource = "mytabis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession =null;
try{
//2.获取sqlSession实例,能直接执行已经映射的SQL语句
sqlSession= sqlSessionFactory.openSession();
//需要两个参数,第一个参数是sql语句的唯一标识,
//第二个参数是执行sql要用的参数
Employee employee = sqlSession.selectOne("com.neuedu.mybatis.EmployeeMapper.selectEmp",1);
System.out.println(employee);
}catch(Exception e){
}finally{
sqlSession.close();
}
}
}
6. 创建sql语句的映射文件EmployeeMapper.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">
<mapper namespace="com.neuedu.mybatis.EmployeeMapper">
<!--
namespace:名称空间
id:sql语句的唯一标识
resultType:返回值类型
#{id}:接收参数传递过来的id值
-->
<select id="selectEmp" resultType="com.neuedu.mybatis.bean.Employee">
select id,last_name lastName,gender,email from tbl_employee where id = #{id}
</select>
</mapper>
7.总结:
HelloWorld简单版
– 创建一张测试表
–创建对应的javaBean
–创建mybatis配置文件,sql映射文件
– 测试
/**
* 1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
* 有数据源的一些运行环境信息
* 2.sql映射文件,配置了每一个sql,以及sql的封装规则等。
* 3.将sql映射文件注册在全局配置文件中
* 4.写代码:
* 1)根据全局配置文件得到SqlSessionFactory
* 2)使用sqlSession工厂,获取到sqlSession对象使用它来执行增删改查!
* sqlSession就是代表和数据库的一次会话!用完要关闭!
* 3)使用sql的唯一标识告诉MyBatis执行哪个sql。而sql都是保存
* 在sql映射文件中的。
*/
上面那种开发方式适合老版本的mybatis使用者的开发方式!而新一批的mybatis使用者都是使用接口的方式,如下所示:
8.HelloWorld-接口式编程
– 创建一个Dao接口
– 修改Mapper文件
– 测试
以前都是需要为接口写一个实现类,这是以前,但是此时,mybatis提供了接口可以与sql配置文件动态绑定!
那如何将两者进行绑定呢?以前sql配置文件的namespace可以随便写,现在就不能随便写了,需呀指定为接口的全限定名!
然后此时接口和sql配置文件做了绑定,然后还要将select标签的id和方法名进行绑定,
总结:
1.接口式编程
原生: Dao ==================> DaoImpl
mybatis: Mapper ================> xxMapper.xml
2.SqlSession代表和数据库的一次会话,用完必须关闭。
3.SqlSession和Connection一样,都是非线程安全的,每次使用都是应该去获取新的对象,不要将这个对象定义在类变量中使用!
4.mapper接口没有实现类,但是mybatis这个接口生成一个代理对象
<!--将接口与xml文件进行绑定 -->
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
5.两个重要的配置文件
mybatis的全局配置文件:包含数据库连接池信息,事务管理器信息等..系统运行环境信息。
sql映射文件:保存了每一个sql语句的映射信息。
三、MyBatis-全局配置文件
?MyBatis 的配置文件包含了影响 MyBatis 行为甚深的 设置(settings)和属性(properties)信息。文档的 顶层结构如下:
?configuration 配置
?properties 属性
?settings 设置
?typeAliases 类型命名
?typeHandlers 类型处理器
?objectFactory 对象工厂
?plugins 插件
?environments 环境
?environment 环境变量
?transactionManager 事务管理器
?dataSource 数据源
?databaseIdProvider 数据库厂商标识
?mappers 映射器
1.为全局配置文件绑定dtd约束:
1)联网会自动绑定
2)没网的时候【/org/apache/ibatis/builder/xml/mybatis-3-config.dtd】:解压mybatis 的jar包然后在eclipse中绑定
2. properties属性
<configuration>
<!--
1.mybatis可以使用properties来引入外部properties配置文件的内容
resource:引入类路径下的资源
url:引入网络路径或者磁盘路径下的资源
-->
<properties resource="jdbc.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.passowrd}"/>
</dataSource>
</environment>
</environments>
<!-- 将我们写好的sql映射文件一定要注册到全局配置文件中 -->
<mappers>
<mapper resource="EmployeeMapper.xml"/>
</mappers>
</configuration>
3.settings包含很多重要的设置项
setting:用来设置每一个设置
name:设置项名
value:设置项取值
举例:驼峰式命名
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
4.typeAliases
作用:A type alias is simply a shorter name for a Java type
<!-- typeAliases:别名处理器,可以为我们的java类型起别名,别名不区分大小写 -->
<typeAliases>
<!-- typeAlias:为某个java类型起别名
type:指定要起别名的类型全类名;默认别名就是类名小写;
alias:执行新的别名
-->
<typeAlias type="com.neuedu.mybatis.bean.Employee"/>
<!--
package:为某个包下的所有类批量起别名
name:指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名【类名小写】)
-->
<package name="com.neuedu.mybatis.bean"/>
<!-- 批量起别名的情况下,使用@Alias注解为某个类型指定新的别名 -->
</typeAliases>
虽然有这么多的别名可以使用:但是建议大家还是使用全类名,看SQL语句是怎么被封装为JAVA 对象的时候简单!
5.typeHandlers:类型处理器
类型处理器:负责如何将数据库的类型和java对象类型之间转换的工具类
6.environments
<!--
environments:环境们,mybatis可以配置多种环境,default指定使用某种环境。可以达到快速切换环境。
environment:配置一个具体的环境信息;必须有两个标签;id代表当前环境的唯一标识
transactionManager:事务管理器
type:事务管理器的类型;type="[JDBC|MANAGED]"),这两个都是别名,在Configuration类中可以查看具体类!但是Spring对事务的控制才是最终的管理方案!
当然也可以自定义事务管理器:只需要和人家一样实现TransactionFactory接口,type指定为全类名。
dataSource:数据源
type:type="[UNPOOLED|POOLED|JNDI]"
自定义数据源:实现DataSourceFactory接口,type也是全类名
但是我们也说了,无论是事务管理器的配置还是数据源的配置我们都会使
用spring来做,这里只需要了解一下即可!
-->
<environments default="development">
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.passowrd}"/>
</dataSource>
</environment>
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.passowrd}"/>
</dataSource>
</environment>
</environments>
7.databaseIdProvider环境
MyBatis is able to execute different statements depending on your database vendor. The
? MyBatis 可以根据不同的数据库厂商执行不同的语句
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="" />
</databaseIdProvider>
?Type: DB_VENDOR
–使用MyBatis提供的VendorDatabaseIdProvider解析数据库 厂商标识。也可以实现DatabaseIdProvider接口来自定义。
?Property-name:数据库厂商标识
?Property-value:为标识起一个别名,方便SQL语句使用
在mybatis的全局配置文件配置了这个之后,我们只需要在sql映射文件中通过在执行语句的标签上加一个属性databaseId即可!
<select id="getEmployeeById" resultType="emp">
select * from tbl_employee where id = #{id}
</select>
<select id="getEmployeeById" resultType="emp" databaseId="mysql">
select * from tbl_employee where id = #{id}
</select>
<select id="getEmployeeById" resultType="emp" databaseId="oracle">
select * from tbl_employee where id = #{id}
</select>
这样在执行不同数据库的时候,就会执行不同数据库的语句了!当然如上所示:当有指定
了databaseId属性的和没有指定databaseId属性的,都有的情况下那就按着有指定databaseId属性的sql语句执行!
<environments default="dev_mysql">
<environment id="dev_oracle">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.passowrd}"/>
</dataSource>
</environment>
<environment id="dev_mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.passowrd}"/>
</dataSource>
</environment>
</environments>
8.mapper映射
<!-- mappers:将sql映射注册到全局配置中 -->
<mappers>
<!-- mapper:注册一个sql映射
注册配置文件:
resource:引用类路径下的sql映射文件
mybatis/mapper/EmployeeMapper.xml
url:引用网络路径下或者磁盘路径下的sql映射文件
url="file:///var/mappers/AuthorMapper.xml"
注册接口
class:引用(注册)接口
1.有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一个目录下;
2.没有sql映射文件,所有的sql都是利用注解写在接口上;
推荐:
比较重要的,复杂的Dao接口我们来写sql映射文件
不重要,见到的Dao接口为了开发快速可以使用注解
-->
<mapper resource="mybatis/mapper/EmployeeMapper.xml"/>
<mapper class="com.neuedu.mybatis.mapper.EmployeeMapperAnnotation"/>
<!-- 批量注册:
1.注解版肯定是没问题的
2.但是对于Mapper.xml映射文件和接口分开的,就需要保证在同一个包下了,否则找不到 -->
<package name="com.neuedu.mybatis.mapper"/>
</mappers>
9.最后就是全局配置文件中标签实际上是有顺序的!
四、MyBatis-映射文件
映射文件指导着MyBatis如何进行数据库增删改查, 有着非常重要的意义;
?cache –命名空间的二级缓存配置
?cache-ref – 其他命名空间缓存配置的引用。
?resultMap – 自定义结果集映射
?parameterMap – 已废弃!老式风格的参数映射
?sql –抽取可重用语句块。
?insert – 映射插入语句
?update – 映射更新语句
?delete – 映射删除语句
?select – 映射查询语句
1.先看增删改查标签
接口类:
public interface EmployeeMapper {
public Employee getEmployeeById(Integer id);
public void addEmp(Employee employee);
public void updateEmp(Employee employee);
public void deleteEmp(Integer id);
}
在其对应的sql映射文件中:
<!-- public void addEmp(Employee employee); -->
<!--parameterType:可以省略 ,且该sql语句最后不用写分号-->
<insert id="addEmp" parameterType="com.neuedu.mybatis.bean.Employee">
insert into tbl_employee(last_name,email,gender)
values(#{lastName},#{gender},#{email})
</insert>
<!-- public void updateEmp(Employee employee); -->
<update id="updateEmp">
update tbl_employee
set last_name=#{lastName},email=#{email},gender=#{gender}
where id = #{id}
</update>
<!-- public void deleteEmp(Integer id); -->
<delete id="deleteEmp">
delete from tbl_employee where id = #{id}
</delete>
编写测试单元:
/**
* 测试增删改
* 1.mybatis允许增删改直接定义以下类型返回值
* Integer、Long、Boolean
* 增删改返回的是影响的多少行,只要影响0行以上就会返回true,0行以下就会返回false!
* 直接在接口上写这些包装类或者基本类型就好,
* 没必要在sql映射文件中写resultType,而且在sql映射文件中也没有resultType标签!
* 2.我们需要手动提交数据
*sqlSessionFactory.openSession();===>手动提交
*sqlSessionFactory.openSession(true);===>自动提价
*/
@Test
public void test02() {
String resource = "mytabis-config.xml";
InputStream inputStream;
SqlSession openSession = null;
try {
inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//1.获取到的sqlSession不会自动提交数据,需要手动提交
openSession = sqlSessionFactory.openSession();
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
mapper.addEmp(new Employee(null,"hah","email","1"));
//手动提交
openSession.commit();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//关闭会话
openSession.close();
}
}
2.获取自增主键值
<!--parameterType:可以省略 ,且该sql语句最后不用写分号;
获取自增主键的值:
mysql支持自增主键:自增主键值的获取,mybatis也是利用statement.getGeneratedKeys()
useGeneratedKeys = "true";使用自增主键获取主键值策略
keyProperty:指定对应的主键属性,也就是mybatis获取到主键值以后,将
这个值封装给javaBean的哪个属性
-->
<insert id="addEmp" parameterType="com.neuedu.mybatis.bean.Employee"
useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee(last_name,email,gender)
values(#{lastName},#{gender},#{email})
</insert>
五:参数处理
单个参数:mybatis不会做特殊处理
#{参数名}: 取出参数值
多个参数:mybatis会做特殊处理
多个参数会被封装成一个map,
key:param1...paramN,或者参数的索引也可以
value:传入的参数值
#{}就是从map中获取指定的key的值
异常:
org.apache.ibatis.binding.BingdingException:
Parameter 'id' not found.
Available parameters are [1,0,param1,param2]
操作:
方法:public Employee getEmployeeAndLastName(Integer id,String lastName);
取值:#{id},#{lastName}
命名参数:明确指定封装参数时map的key:@param("id")
多个参数会被封装成一个map,
key:使用@Param注解指定的值
value:参数值
#{指定的key}取出对应的参数值
POJO:
如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo;
#{属性名}:取出传入的pojo的属性值
Map:
如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以传入map
#{key}:取出map中对应的值
List、Set
#这里面的值也会被封装到map集合中:
key:collection
值:对应的参数值
#{collection[0]}或#{list[0]}
六、参数值的获取
#{}:可以获取map中的值或者pojo对象属性的值
${}: 可以获取map中的值获取pojo对象属性的值
select * from tbl_employee where id = ${id} and last_name = #{lastName}
preparing:select * from tbl_employee where id = 2 and last_name = ?
区别:
#{}:是以预编译的形式,将参数设置到sql语句中,PreparedStatement;防止sql注入
${}:取出的值直接拼装在sql语句中,会有安全问题;
大多情况下,我们取参数的值都应该去使用#{};
原生JDBC不支持占位符的地方我们就可以使用${}进行取值
比如分表、排序;按照年份分表拆分
select * from ${year}_salary where xxx;[表名不支持预编译]
select * from tbl_employee order by ${f_name} ${order} :排序是不支持预编译的!
select元素
? Select元素来定义查询操作。
? Id:唯一标识符。
– 用来引用这条语句,需要和接口的方法名一致
?parameterType:参数类型。
–可以不传,MyBatis会根据TypeHandler自动推断
?resultType:返回值类型。
– 别名或者全类名,如果返回的是集合,定义集合中元素的类型。不能和resultMap同时使用
1.返回类型为一个List
eg:public List<Employee> getEmpsByLastNameLike(String lastName);
<!-- resultType:如果返回的是一个集合,要写集合中元素的类型 -->
<selecct id="getEmpsByLastNameLike" resultType="com.neuedu.mybatis.bean.Employee">
select * from tbl_employee where lastName like #{lastName}
</select>
2.返回记录为一个Map
<!-- 返回一条记录的map:key就是列名,值就是对应的值 -->
public Map<String,Object> getEmpByIdReturnMap(Integer id);
<select id="getEmpByIdReturnMap" resultType="map">
select * from tbl_employee where id = #{id}
</select>
3.返回为一个ResultMap:
<!-- resultMap:自定义结果集映射规则 -->
public Employee getEmployeById(Integer id);
<!-- 自定义某个javaBean的封装规则
type:自定义规则的java类型
id:唯一id方便引用
-->
<resultMap type="com.neuedu.mybatis.bean.Employee" id = "myEmp">
<!--
指定主键列的封装规则
id定义主键列会有底层优化
column:指定是哪一列
property:指定对应的javaBean属性
-->
<id column="id" property = "id">
<!-- 定义普通列封装规则 -->
<result column="last_name" property="lastName"/>
<!--其它不指定的列只要属性名和列名会自动封装,我们只要写resultMap就把全部的映射规则都写上 -->
</resultMap>
<!--只需要在映射的地方映射一下就可以了 --》