1、在Eclipse中引入xml DTD约束文件,让Eclipse可以给提示
进入bybatis全局配置文件,复制DTD引入标签到剪贴板
window—preferences
进入XML Catalog 点击Add,然后按照图示步骤即可完成添加
之后在全局文件中操作,就会有提示
2、如何写配置文件
官方文档第三章 Configuration XML就会告诉你可以写哪些(尽量看官方文档,毕竟这东西随时更新的)
特别注意:标签有先后顺序,顺序乱了会报错(就按照教学的顺序就是了)
2.1 properties标签(了解即可,后面spring整合mybatis后,会用spring做这些事)
mybatis可以使用properties来引入外部properties配置文件的内容;这样可以不改变代码,就换数据库
properties标签
属性:
resource:引入类路径下的资源
url:引入网络路径或者磁盘路径下的资源由于数据库相关元素都写在了配置文件中,配置文件中可以用EL表达式提取出来
<properties resource="dbconfig.properties"></properties>
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
在项目的conf文件夹下编写 配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=123456
创建package com.atguigu.mybatis.test包,
编写测试文件
package com.atguigu.mybatis.test;
public class MyBatisTest {
@Test
public void test01() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpById(1);
System.out.println(mapper.getClass());
System.out.println(employee);
} finally {
openSession.close();
}
}
}
2.2 settings标签(看官网文档为主https://mybatis.org/mybatis-3/zh/configuration.html#settings,这里只讲典型)
这是MyBatis 中极为重要的调整设置,它们会改变MyBatis 的运行时行为。
(1)
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true|false(默认) |
如果开启自动驼峰命名,只要数据库的字段满足 A_COLUMN,就可以自动匹配java属性名aColumn,不用通过取别名的方式来解决下划线问题了。
settings包含很多重要的设置项
setting:用来设置每一个设置项
name:设置项名
value:设置项取值
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
2.3typeAliases标签 别名处理器(建议还是乖乖写全类名,因为idea索引会非常方便)
类型别名是为Java 类型设置一个短的名字,可以方便我们引用某个类。由于配置文件要求写完整的 包+类名 ,有的全类名就会非常的长,typeAliases就是给这些长标签取个简单别名,让其精简
进入全局配置文件
<typeAliases>
<!-- 1、typeAlias:为某个java类型起别名,别名不区分大小写
type:指定要起别名的类型全类名;默认别名就是类名小写;employee
alias:指定新的别名
-->
<typeAlias type="com.atguigu.mybatis.bean.Employee" alias="emp"/>
</typeAliases>
package标签
批量起别名
但是如果有几十个类,一个一个写标签取别名工作量也非常大,因此可以批量取别名
package标签,可是package标签默认就是把类名小写,别名又不区分大小写,这样如果同一包下两个类刚好叫User,和user,就会别名冲突。
<!-- 2、package:为某个包下的所有类批量起别名
name:指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名(类名小写),)
-->
<package name="com.atguigu.mybatis.bean"/>为了解决别名冲突光是这个标签还不够,还需要加入@Alias注解
<!-- 3、批量起别名的情况下,使用@Alias注解为某个类型指定新的别名 -->
package com.atguigu.mybatis.bean;
@Alias("emp") //将package com.atguigu.mybatis.bean.Employee 别名成 emp
public class Employee {
private Integer id;
private String lastName;
private String email;
private String gender;
}
值得注意的是,MyBatis已经为许多常见的Java 类型内建了相应的类型别名。它们都是大小写不敏感的,我们在起别名的时候千万不要占用已有的别名。所有请查官网手册
取完别名后,进入com/atguigu/mybatis/dao EmployeeMapper.xml,里面的resultType就可以用别名啦
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapper">
<!--
namespace:名称空间;指定为接口的全类名
id:唯一标识
resultType:返回值类型
#{id}:从传递过来的参数中取出id值public Employee getEmpById(Integer id);
-->
<select id="getEmpById" resultType="emp"> //这里就可以用别名啦
select * from tbl_employee where id = #{id}
</select></mapper>
2.4 typeHandlers标签 (类型处理器)
将数据库类型(比如 VARCHAR,Date)等映射成java的(int,String)等类型
后面再细讲
2.5 plugins标签 (插件)
2.5.1 插件介绍
插件是MyBatis提供的一个非常强大的机制,我们可以通过插件来修改MyBatis的一些核心行为。插件通过动态代理机制(类似Spring的AOP),可以介入四大对象的任何一个方法的执行。结合前一章的运行原理学习插件
四大核心对象:
增删改,事务:Executor(update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
参数处理器:ParameterHandler(getParameterObject, setParameters)
结果集处理器:ResultSetHandler(handleResultSets, handleOutputParameters)
SQL语句处理器:StatementHandler(prepare, parameterize, batch, update, query)
MyBatis在四大对象的创建过程中,都会有插件进行介入。插件可以利用动态代理机制一层层的包装目标对象,而实现在目标对象执行目标方法之前进行拦截的效果。
2.5.2 插件原理
1)、按照插件注解声明,按照插件配置顺序调用插件plugin方法,生成被拦截对象的动态代理
2)、多个插件依次生成目标对象的代理对象,层层包裹,先声明的先包裹;形成代理链
3)、目标方法执行时依次从外到内执行插件的intercept方法。
4)、多个插件情况下,我们往往需要在某个插件中分离出目标对象。可以借助MyBatis提供的SystemMetaObject类来进行获取最后一层的h以及target属性的值
* 在四大对象创建的时候
* 1、每个创建出来的对象不是直接返回的,而是
* interceptorChain.pluginAll(parameterHandler);
* 2、获取到所有的Interceptor(拦截器)(插件需要实现的接口);
* 调用interceptor.plugin(target);返回target包装后的对象
* 3、插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP(面向切面)
* 我们的插件可以为四大对象创建出代理对象;
* 代理对象就可以拦截到四大对象的每一个执行;
*
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
2.5.3 插件开发步骤
演示:想查询1号员工,结果返回11号员工
step1 进入com.atguigu.mybatis.dao包中,编写插件类(实现Interceptor接口)
(1)编写Interceptor的实现类
(2)使用@Intercepts注解完成插件签名(告诉MyBatis当前插件用来拦截哪个对象的哪个方法)
(3)将写好的插件注册到全局配置文件中
Interceptor接口
•Intercept:拦截目标方法执行
•plugin:生成动态代理对象,可以使用MyBatis提供的Plugin类的wrap方法
•setProperties:注入插件配置时设置的属性//1、分离代理对象。由于会形成多次代理,所以需要通过一个while循环分离出最终被代理对象,从而方便提取信息
MetaObject metaObject = SystemMetaObject.forObject(target);
while(metaObject.hasGetter("h")) {
Object h = metaObject.getValue("h");
metaObject = SystemMetaObject.forObject(h);
}
//2、获取到代理对象中包含的被代理的真实对象
Object obj = metaObject.getValue("target");
//3、获取被代理对象的MetaObject方便进行信息提取
MetaObject forObject = SystemMetaObject.forObject(obj);
@Intercepts(
{
@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
})
public class MyFirstPlugin implements Interceptor{
/**
* intercept:拦截:
* 拦截目标对象的目标方法的执行;
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
// TODO Auto-generated method stub
System.out.println("MyFirstPlugin...intercept:"+invocation.getMethod());
//动态的改变一下sql运行的参数:以前1号员工,实际从数据库查询3号员工
Object target = invocation.getTarget();
System.out.println("当前拦截到的对象:"+target);
//拿到:StatementHandler==>ParameterHandler===>parameterObject
//拿到target的元数据
MetaObject metaObject = SystemMetaObject.forObject(target);
Object value = metaObject.getValue("parameterHandler.parameterObject");
System.out.println("sql语句用的参数是:"+value);
//修改完sql语句要用的参数
metaObject.setValue("parameterHandler.parameterObject", 11);
//执行目标方法
Object proceed = invocation.proceed();
//返回执行后的返回值
return proceed;
}
/**
* plugin:
* 包装目标对象的:包装:为目标对象创建一个代理对象
*/
@Override
public Object plugin(Object target) {
// TODO Auto-generated method stub
//我们可以借助Plugin的wrap方法来使用当前Interceptor包装我们目标对象
System.out.println("MyFirstPlugin...plugin:mybatis将要包装的对象"+target);
Object wrap = Plugin.wrap(target, this);
//返回为当前target创建的动态代理
return wrap;
}
/**
* setProperties:
* 将插件注册时 的property属性设置进来
*/
@Override
public void setProperties(Properties properties) {
// TODO Auto-generated method stub
System.out.println("插件配置的信息:"+properties);
}
}
第二个插件,MySecondPlugin.java
package com.atguigu.mybatis.dao;
@Intercepts(
{
@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
})
public class MySecondPlugin implements Interceptor{
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("MySecondPlugin...intercept:"+invocation.getMethod());
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
// TODO Auto-generated method stub
System.out.println("MySecondPlugin...plugin:"+target);
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// TODO Auto-generated method stub
}
}
全局配置文件中
<!--plugins:注册插件 -->
<plugins>
<plugin interceptor="com.atguigu.mybatis.dao.MyFirstPlugin">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</plugin>
<plugin interceptor="com.atguigu.mybatis.dao.MySecondPlugin"></plugin>
</plugins>
用test01测试即可
package com.atguigu.mybatis.test;
public class MyBatisTest {
@Test
public void test01() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpById(1);
System.out.println(mapper);
System.out.println(employee);
} finally {
openSession.close();
}
}
}
2.6 environments标签 运行环境
(1)MyBatis可以配置多种环境,比如开发、测试和生产环境需要有不同的配置。
(2)每种环境使用一个environment标签进行配置并指定唯一标识符
environment:配置一个具体的环境信息;必须有两个标签;id属性代表当前环境的唯一标识
① transactionManager标签:事务管理器(了解下即可,后面也是交给spring做)
——type属性:事务管理器的类型 JDBC | MANAGED | 自定义
❶ JDBC(默认):使用了JDBC 的提交和回滚设置,依赖于从数据源得到的连接来管理事务范围。JdbcTransactionFactory
❷ MANAGED:不提交或回滚一个连接、让容器来管理事务的整个生命周期(比如JEE 应用服务器的上下文)。ManagedTransactionFactory
❸ 自定义:实现TransactionFactory接口,type=全类名/别名
数据源类型:果断选用连接池技术(默认)
② dataSource 数据源标签
type属性:UNPOOLED | POOLED | JNDI | 自定义
❶ UNPOOLED:不使用连接池,UnpooledDataSourceFactory
❷ POOLED:使用连接池,PooledDataSourceFactory
❸ JNDI:在EJB 或应用服务器这类容器中查找指定的数据源
❹ 自定义:实现DataSourceFactory接口,定义数据源的获取方式。比如想用JDBC中的其他连接池技术,就可以通过配置数据源来切换
(3)可以通过environments标签中的default属性指定一个环境的标识符来快速的切换环境
说明:开发人员需要开发环境,但是测试人员需要连接测试数据库,就可以通过该标签快速切换
代码演示
<environments default="dev_mysql">
<environment id="dev_mysql">
<transactionManager type="JDBC"></transactionManager>
<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>
<environment id="dev_oracle">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${orcl.driver}" />
<property name="url" value="${orcl.url}" />
<property name="username" value="${orcl.username}" />
<property name="password" value="${orcl.password}" />
</dataSource>
</environment>
</environments>
2.7 dataseIdProvider标签(支持多数据库厂商)
该标签是mybatis在移植性方面的支持,通过这个标签, MyBatis 可以根据不同的数据库厂商执行不同的语句。
我们只需要通过该标签告诉mybatis我们用的哪个厂家数据库即可,剩下由mybatis自动完成
Type:DB_VENDOR
–使用MyBatis提供的VendorDatabaseIdProvider解析数据库厂商标识。也可以实现DatabaseIdProvider接口来自定义。
•Property-name:数据库厂商标识
•Property-value:为标识起一个别名,方便SQL语句使用databaseId属性引用
数据库映射文件中:
案例演示,oracle 和 mysql 动态切换
全局配置文件中,配置好想要的数据库,并在环境模式中(environments)选择该数据库环境
step1:环境中写好数据库。default属性可以快速切换环境(environment标签id)
<environments default="dev_mysql">
<environment id="dev_mysql">
<transactionManager type="JDBC"></transactionManager>
<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>
<environment id="dev_oracle">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${orcl.driver}" />
<property name="url" value="${orcl.url}" />
<property name="username" value="${orcl.username}" />
<property name="password" value="${orcl.password}" />
</dataSource>
</environment>
</environments>
<!-- 5、databaseIdProvider:支持多数据库厂商的;
type="DB_VENDOR":VendorDatabaseIdProvider
作用就是得到数据库厂商的标识(驱动getDatabaseProductName()),mybatis就能根据数据库厂商标识来执行不同的sql;
MySQL,Oracle,SQL Server,xxxx
-->
<databaseIdProvider type="DB_VENDOR">
<!-- 为不同的数据库厂商起别名 -->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
进入\conf\com\atguigu\mybatis\dao EmployeeMapper.xml配置文件,通过databaseId属性告诉mybatis是哪个厂家的数据库
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapper">
<!--
databaseId属性,告诉mybatis这句sql是哪个厂家的
-->
<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee where id = #{id}
</select>
<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee"
databaseId="mysql">
select * from tbl_employee where id = #{id}
</select>
<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee"
databaseId="oracle">
select EMPLOYEE_ID id,LAST_NAME lastName,EMAIL email
from employees where EMPLOYEE_ID=#{id}
</select>
</mapper>
工程目录/conf文件夹下,dbconfig.properties文件中,写好orcale的相关配置
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=123456orcl.driver=oracle.jdbc.OracleDriver
orcl.url=jdbc:oracle:thin:@localhost:1521:orcl
orcl.username=scott
orcl.password=123456
进MyBatisest.java文件夹测试效果
package com.atguigu.mybatis.test;
public class MyBatisTest {
@Test
public void test01() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpById(1);
System.out.println(mapper.getClass());
System.out.println(employee);
} finally {
openSession.close();
}
}
}
2.8 mappers标签
mapper逐个注册SQL映射文件
说明:
(1)mappers标签:将sql映射注册到全局配置中
子标签mapper:注册一个sql映射
resource属性:引用类路径下的sql映射文件 mybatis/mapper/EmployeeMapper.xml
url:引用网路路径或者磁盘路径下的sql映射文件 file:///var/mappers/AuthorMapper.xml
class属性:引用(注册)接口,
1、有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
2、没有sql映射文件,所有的sql都是利用注解写在接口上;(备注:为了开发代码与配置完全分离,不改变代码,建议不用注解写sql)
(2)批量注册,这种方式要求SQL映射文件名必须和接口名相同并且在同一目录下
小技巧:由于配置文件和代码写在一起,多了,很不好看。我们只需要在配置文件夹conf和src文件夹下创建相同的包名,即可批量注册接口。因为源码文件夹里面的东西最终都会被一起编译到类路径下(bin目录)
<!-- 6、mappers:将sql映射注册到全局配置中 -->
<mappers>
<!--
mapper:注册一个sql映射
注册配置文件
resource:引用类路径下的sql映射文件
mybatis/mapper/EmployeeMapper.xml
url:引用网路路径或者磁盘路径下的sql映射文件
file:///var/mappers/AuthorMapper.xml
注册接口
class:引用(注册)接口,
1、有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
2、没有sql映射文件,所有的sql都是利用注解写在接口上;
推荐:
比较重要的,复杂的Dao接口我们来写sql映射文件
不重要,简单的Dao接口为了开发快速可以使用注解;
-->
<mapper resource="mybatis/mapper/EmployeeMapper.xml"/>
<mapper class="com.atguigu.mybatis.dao.EmployeeMapperAnnotation"/>
<!--或者批量注册: -->
<package name="com.atguigu.mybatis.dao"/>
</mappers>
演示注解版注册接口 在com.auguigu.mybatis.dao下新建一个接口 EmployeeMapperAnnotation
使用@Select注解,即可写sql语句
package com.atguigu.mybatis.dao;
public interface EmployeeMapperAnnotation {
@Select("select * from tbl_employee where id=#{id}")
public Employee getEmpById(Integer id);
}
进入MyBatisTest.java测试类里面 新写一个test02
package com.atguigu.mybatis.test;
public class MyBatisTest {
@Test
public void test02() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try{
EmployeeMapperAnnotation mapper = openSession.getMapper(EmployeeMapperAnnotation.class);
Employee empById = mapper.getEmpById(1);
System.out.println(empById);
}finally{
openSession.close();
}
}
}