Mybatis的介绍和xml配置
本文code地址:
https://gitee.com/caimengzhi/code_java/tree/master/mybatis
1. 数据库操作框架的历程
1.1 JDBC
JDBC(Java Data Base Connection,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成.JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序
- 优点:运行期:快捷、高效
- 缺点:编辑期:代码量大、繁琐异常处理、不支持数据库跨平台
1.2 DBUtils
DBUtils是Java编程中的数据库操作实用工具,小巧简单实用。DBUtils封装了对JDBC的操作,简化了JDBC操作,可以少写代码。
DBUtils三个核心功能介绍
- QueryRunner中提供对sql语句操作的API
- ResultSetHandler接口,用于定义select操作后,怎样封装结果集
- DBUtils类,它就是一个工具类,定义了关闭资源与事务处理的方法
1.3 Hibernate
Hibernate 是由 Gavin King 于 2001 年创建的开放源代码的对象关系框架。它强大且高效的构建具有关系对象持久性和查询服务的 Java 应用程序。
Hibernate 将 Java 类映射到数据库表中,从 Java 数据类型中映射到 SQL 数据类型中,并把开发人员从 95% 的公共数据持续性编程工作中解放出来。
Hibernate 是传统 Java 对象和数据库服务器之间的桥梁,用来处理基于 O/R 映射机制和模式的那些对象。
Hibernate 优势
Hibernate 使用 XML 文件来处理映射 Java 类别到数据库表格中,并且不用编写任何代码。
为在数据库中直接储存和检索 Java 对象提供简单的 APIs。
如果在数据库中或任何其它表格中出现变化,那么仅需要改变 XML 文件属性。
抽象不熟悉的 SQL 类型,并为我们提供工作中所熟悉的 Java 对象。
Hibernate 不需要应用程序服务器来操作。
操控你数据库中对象复杂的关联。
最小化与访问数据库的智能提取策略。
提供简单的数据询问。
Hibernate劣势
hibernate的完全封装导致无法使用数据的一些功能。
Hibernate的缓存问题。
Hibernate对于代码的耦合度太高。
Hibernate寻找bug困难。
Hibernate批量数据操作需要大量的内存空间而且执行过程中需要的对象太多
(4) JDBCTemplate
JdbcTemplate针对数据查询提供了多个重载的模板方法,你可以根据需要选用不同的模板方法.如果你的查询很简单,仅仅是传入相应SQL或者相关参数,然后取得一个单一的结果,那么你可以选择如下一组便利的模板方法。
优点:运行期:高效、内嵌Spring框架中、支持基于AOP的声明式事务
缺点:必须于Spring框架结合在一起使用、不支持数据库跨平台、默认没有缓存
2. Mybatis
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
2.1 优点
- 与JDBC相比,减少了50%的代码量
- 最简单的持久化框架,简单易学
- SQL代码从程序代码中彻底分离出来,可以重用
- 提供XML标签,支持编写动态SQL
- 提供映射标签,支持对象与数据库的ORM字段关系映射
2.2 缺点
- SQL语句编写工作量大,熟练度要高
- 数据库移植性比较差,如果需要切换数据库的话,SQL语句会有很大的差异
3. Mybatis项目
3.1 创建普通的maven项目
3.2 导入相关的依赖
pom.xml
的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cmz</groupId>
<artifactId>mybatis_demo1</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
3.3 创建对应的数据表
数据表我们使用之前的demo数据库
/*
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MySQL
Source Server Version : 50709
Source Host : localhost:3306
Source Schema : demo
Target Server Type : MySQL
Target Server Version : 50709
File Encoding : 65001
Date: 09/04/2020 14:45:02
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for account
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`username` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`balance` double NULL DEFAULT NULL,
PRIMARY KEY (`username`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES ('lisi', 1000);
INSERT INTO `account` VALUES ('zhangsan', 900);
-- ----------------------------
-- Table structure for book
-- ----------------------------
DROP TABLE IF EXISTS `book`;
CREATE TABLE `book` (
`id` int(10) NOT NULL,
`book_name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`price` double NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of book
-- ----------------------------
INSERT INTO `book` VALUES (1, '西游记', 100);
INSERT INTO `book` VALUES (2, '水浒传', 100);
INSERT INTO `book` VALUES (3, '三国演义', 100);
INSERT INTO `book` VALUES (4, '红楼梦', 100);
-- ----------------------------
-- Table structure for book_stock
-- ----------------------------
DROP TABLE IF EXISTS `book_stock`;
CREATE TABLE `book_stock` (
`id` int(255) NOT NULL DEFAULT 0,
`stock` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of book_stock
-- ----------------------------
INSERT INTO `book_stock` VALUES (1, 999);
INSERT INTO `book_stock` VALUES (2, 1000);
INSERT INTO `book_stock` VALUES (3, 1000);
INSERT INTO `book_stock` VALUES (4, 1000);
-- ----------------------------
-- Table structure for dept
-- ----------------------------
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
`DEPTNO` int(11) NOT NULL,
`DNAME` varchar(14) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`LOC` varchar(13) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`DEPTNO`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of dept
-- ----------------------------
INSERT INTO `dept` VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO `dept` VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO `dept` VALUES (30, 'SALES', 'CHICAGO');
INSERT INTO `dept` VALUES (40, 'OPERATIONS', 'BOSTON');
-- ----------------------------
-- Table structure for emp
-- ----------------------------
DROP TABLE IF EXISTS `emp`;
CREATE TABLE `emp` (
`EMPNO` int(11) NOT NULL,
`ENAME` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`JOB` varchar(9) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`MGR` double NULL DEFAULT NULL,
`HIREDATE` date NULL DEFAULT NULL,
`SAL` double NULL DEFAULT NULL,
`COMM` double NULL DEFAULT NULL,
`DEPTNO` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`EMPNO`) USING BTREE,
INDEX `DEPTNO`(`DEPTNO`) USING BTREE,
CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`DEPTNO`) REFERENCES `dept` (`DEPTNO`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of emp
-- ----------------------------
INSERT INTO `emp` VALUES (7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800, NULL, 20);
INSERT INTO `emp` VALUES (7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600, 300, 30);
INSERT INTO `emp` VALUES (7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250, 500, 30);
INSERT INTO `emp` VALUES (7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975, NULL, 20);
INSERT INTO `emp` VALUES (7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250, 1400, 30);
INSERT INTO `emp` VALUES (7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850, NULL, 30);
INSERT INTO `emp` VALUES (7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450, NULL, 10);
INSERT INTO `emp` VALUES (7788, 'SCOTT', 'ANALYST', 7566, '1987-07-13', 3000, NULL, 20);
INSERT INTO `emp` VALUES (7839, 'KING', 'PRESIDENT', NULL, '1981-11-17', 5000, NULL, 10);
INSERT INTO `emp` VALUES (7844, 'TURNER', 'SALESMAN', 7698, '1981-09-08', 1500, 0, 30);
INSERT INTO `emp` VALUES (7876, 'ADAMS', 'CLERK', 7788, '1987-07-13', 1100, NULL, 20);
INSERT INTO `emp` VALUES (7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950, NULL, 30);
INSERT INTO `emp` VALUES (7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000, NULL, 20);
INSERT INTO `emp` VALUES (7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1300, NULL, 10);
-- ----------------------------
-- Table structure for salgrade
-- ----------------------------
DROP TABLE IF EXISTS `salgrade`;
CREATE TABLE `salgrade` (
`GRADE` int(11) NULL DEFAULT NULL,
`LOSAL` double NULL DEFAULT NULL,
`HISAL` double NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of salgrade
-- ----------------------------
INSERT INTO `salgrade` VALUES (1, 700, 1200);
INSERT INTO `salgrade` VALUES (2, 1201, 1400);
INSERT INTO `salgrade` VALUES (3, 1401, 2000);
INSERT INTO `salgrade` VALUES (4, 2001, 3000);
INSERT INTO `salgrade` VALUES (5, 3001, 9999);
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL,
`user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'zhangsan');
INSERT INTO `user` VALUES (2, 'lisi ');
SET FOREIGN_KEY_CHECKS = 1;
3.4 建与表对应实体类对象
emp.java
package com.cmz.bean;
import java.util.Date;
/**
* @author summer
* @create 2020-04-08 16:33
*/
public class Emp {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double common;
private Integer deptno;
public Emp() {
}
public Emp(Integer empno, String ename, String job, Integer mgr, Date hiredate, Double sal, Double common, Integer deptno) {
this.empno = empno;
this.ename = ename;
this.job = job;
this.mgr = mgr;
this.hiredate = hiredate;
this.sal = sal;
this.common = common;
this.deptno = deptno;
}
public Integer getEmpno() {
return empno;
}
public void setEmpno(Integer empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Integer getMgr() {
return mgr;
}
public void setMgr(Integer mgr) {
this.mgr = mgr;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
public Double getCommon() {
return common;
}
public void setCommon(Double common) {
this.common = common;
}
public Integer getDeptno() {
return deptno;
}
public void setDeptno(Integer deptno) {
this.deptno = deptno;
}
@Override
public String toString() {
return "Emp{" +
"empno=" + empno +
", ename='" + ename + '\'' +
", job='" + job + '\'' +
", mgr=" + mgr +
", hiredate=" + hiredate +
", sal=" + sal +
", common=" + common +
", deptno=" + deptno +
'}';
}
}
3.5 创建对应的dao类
EmpDao.java
package com.cmz.dao;
import com.cmz.bean.Emp;
/**
* @author summer
* @create 2020-04-08 16:35
*/
public interface EmpDao {
public Integer save(Emp emp);
public Integer update(Emp emp);
public Integer delete(Integer empno);
public Emp selectEmpByEmpno(Integer empno);
}
3.6 编写配置文件
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/demo?serverTimezone=UTC
username=root
password=root
注意: 一定要加serverTimezone=UTC,否则会报错
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>
<properties resource="db.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!--配置数据库连接-->
<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>
<!--引入每一个接口对应点xml文件-->
<mappers>
<mapper resource="EmpDao.xml"/>
</mappers>
</configuration>
EmpDao.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">
<!--namespace:编写接口的全类名,就是告诉要实现该配置文件是哪个接口的具体实现-->
<mapper namespace="com.cmz.dao.EmpDao">
<!--
select:表示这个操作是一个查询操作
id表示的是要匹配的方法的名称
resultType:表示返回值的类型,查询操作必须要包含返回值的类型
#{属性名}:表示要传递的参数的名称
-->
<select id="selectEmpByEmpno" resultType="com.cmz.bean.Emp">
select * from emp where empno = #{empno}
</select>
<insert id="save">
insert into emp(empno,ename) values(#{empno},#{ename})
</insert>
<insert id="update">
update emp set sal=#{sal} where empno=#{empno}
</insert>
<delete id="delete">
delete from emp where empno=#{empno}
</delete>
</mapper>
日志配置
# 全局日志配置
log4j.rootLogger=INFO, stdout
# MyBatis 日志配置
log4j.logger.com.cmz=TRACE
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
执行以下代码
/*查询*/
@Test
public void test05() throws IOException {
//获取与数据库相关的会话
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取对应的映射接口对象
UserDao mapper = sqlSession.getMapper(UserDao.class);
//执行对应的sql
User user = mapper.selectUserById(1);
System.out.println(user);
sqlSession.close();
}
会打印类型如下信息
DEBUG [main] - ==> Preparing: select * from user where id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
TRACE [main] - <== Columns: id, user_name
TRACE [main] - <== Row: 1, zhangsan
DEBUG [main] - <== Total: 1
User{id=1, userName='zhangsan'}
3.7 编写测试类[增删改查]
MyTest.java
import com.cmz.bean.Emp;
import com.cmz.dao.EmpDao;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
/**
* @author summer
* @create 2020-04-08 16:44
*/
public class MyTest {
SqlSessionFactory sqlSessionFactory = null;
@Before
public void init(){
String resource = "mybatis_config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
/*查询*/
@Test
public void test01() throws IOException {
String resource = "mybatis_config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取与数据库相关的会话
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取对应的映射接口对象
EmpDao mapper = sqlSession.getMapper(EmpDao.class);
//执行对应的sql
Emp emp = mapper.selectEmpByEmpno(7369);
System.out.println(emp);
sqlSession.close();
}
/*增加*/
@Test
public void test02(){
/*SqlSession sqlSession = sqlSessionFactory.openSession(true);*/ /*默认是不自动提交事务的。需要自己commit提交*/
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpDao mapper = sqlSession.getMapper(EmpDao.class);
Emp emp = new Emp();
emp.setEmpno(3333);
emp.setEname("张三");
Integer count = mapper.save(emp);/*传入的是对象*/
System.out.println("插入成功条数: "+count);
sqlSession.commit();
sqlSession.close();
}
/*修改*/
@Test
public void test03(){
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpDao mapper = sqlSession.getMapper(EmpDao.class);
Emp emp = new Emp();
emp.setEmpno(3333);
emp.setEname("张三");
emp.setSal(500.0);
Integer count = mapper.update(emp);/*传入的是对象*/
System.out.println("修改成功条数: "+count);
sqlSession.commit();
sqlSession.close();
}
/*删除*/
@Test
public void test04(){
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpDao mapper = sqlSession.getMapper(EmpDao.class);
Emp emp = new Emp();
Integer count = mapper.delete(3333);
System.out.println("删除成功条数: "+count);
sqlSession.commit();
sqlSession.close();
}
}
4 配置文件详解
在mybatis的项目中,我们发现了有一个mybatis-config.xml的配置文件,这个配置文件是mybatis的全局配置文件,用来进行相关的全局配置,在任何操作下都生效的配置。下面我们要针对其中的属性做详细的解释,方便大家在后续使用的时候更加熟练。
配置文件中的配置有先后关系
<!ELEMENT configuration (
properties?,
settings?,
typeAliases?,
typeHandlers?,
objectFactory?,
objectWrapperFactory?,
reflectorFactory?,
plugins?,
environments?,
databaseIdProvider?,
mappers?)>
从上到下书写,否则会报错。你可以某个标签没有,但是顺序不能错。
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>
<!--引入外部配置文件,类似于Spring中的property-placeholder
resource:从类路径引入
url:从磁盘路径或者网络路径引入
-->
<properties resource="db.properties"></properties>
<!--用来控制mybatis运行时的行为,是mybatis中的重要配置-->
<settings>
<!--设置列名映射的时候是否是驼峰标识-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--typeAliases表示为我们引用的实体类起别名,默认情况下我们需要写类的完全限定名
如果在此处做了配置,那么可以直接写类的名称,在type中配置上类的完全限定名,在使用的时候可以忽略大小写
还可以通过alias属性来表示类的别名
-->
<typeAliases>
<!-- <typeAlias type="com.mashibing.bean.Emp" alias="Emp"></typeAlias>-->
<!--如果需要引用多个类,那么给每一个类起别名肯定会很麻烦,因此可以指定对应的包名,那么默认用的是类名-->
<package name="com.mashibing.bean"/>
</typeAliases>
<!--
在实际的开发过程中,我们可能分为开发环境,生产环境,测试环境等等,每个环境的配置可以是不一样的
environment就用来表示不同环境的细节配置,每一个环境中都需要一个事务管理器以及数据源的配置
我们在后续的项目开发中几乎都是使用spring中配置的数据源和事务管理器来配置,此处不需要研究
-->
<!--default:用来选择需要的环境-->
<environments default="development">
<!--id:表示不同环境的名称-->
<environment id="development">
<transactionManager type="JDBC"/>
<!--配置数据库连接-->
<dataSource type="POOLED">
<!--使用${}来引入外部变量-->
<property name="driver" value="${driverClassname}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--
在不同的数据库中,可能sql语句的写法是不一样的,为了增强移植性,可以提供不同数据库的操作实现
在编写不同的sql语句的时候,可以指定databaseId属性来标识当前sql语句可以运行在哪个数据库中
-->
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="SQL Server" value="sqlserver"/>
<property name="Oracle" value="orcl"/>
</databaseIdProvider>
<!--将sql的映射文件适用mappers进行映射-->
<mappers>
<!--
指定具体的不同的配置文件
class:直接引入接口的全类名,可以将xml文件放在dao的同级目录下,并且设置相同的文件名称,同时可以使用注解的方式来进行相关的配置
url:可以从磁盘或者网络路径查找sql映射文件
resource:在类路径下寻找sql映射文件
-->
<!-- <mapper resource="EmpDao.xml"/>
<mapper resource="UserDao.xml"/>
<mapper class="com.mashibing.dao.EmpDaoAnnotation"></mapper>-->
<!--
当包含多个配置文件或者配置类的时候,可以使用批量注册的功能,也就是引入对应的包,而不是具体的配置文件或者类
但是需要注意的是,
1、如果使用的配置文件的形式,必须要将配置文件跟dao类放在一起,这样才能找到对应的配置文件.
如果是maven的项目的话,还需要添加以下配置,原因是maven在编译的文件的时候只会编译java文件
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
2、将配置文件在resources资源路径下创建跟dao相同的包名
-->
<package name="com.mashibing.dao"/>
</mappers>
</configuration>
4.1 起别名
别名不区分大小写,但是推荐全类名写法
4.1.1 typeAliases单个别名
表示在引入实体类的名称时候,可以使用别名,而不需要写完全限定名
<!-- typeAlias: 为某个java类型起别名
type: 指定要起别名的类型全类名。默认别名是类名小写,emp
alias: 指定新的别名,别名不区分大小写。
-->
<typeAliases>
<typeAlias type="com.cmz.bean.Emp" alias="Emp"></typeAlias>
</typeAliases>
没修改上述之前,EmpDao.xml中必须指定全路径。如下
<select id="selectEmpByEmpno" resultType="com.cmz.bean.Emp"> <!--没有别名之前-->
加入以上别名之后配置如下:
<select id="selectEmpByEmpno" resultType="Emp"> /*添加别名之后,可以简写*/
其中别名Emp是随便你起的。以下是视频操作演示。
4.1.2 package批量别名
- 包下没有相同的类
上面提到了,可以通过别名来传递,但是以上是针对每个类去做的,每一个具体的类都需要单独来写,如果有100个类呢?那此时就需要以下方式来写了,可以指定具体的包来保证实体类不需要写完全限定名。
mybatis_config.xml
中
<typeAliases>
<!--可以指定具体的包来保证实体类不需要写完全限定名
name: 指定包名(为当前包以及包下面所有的后代包的每一个类都起一个默认别名[类名小写])
-->
<package name="com.cmz.bean"/>
</typeAliases>
运行那个测试类查询效果一样
DEBUG [main] - ==> Preparing: /*添加别名之后,可以简写*/ select * from emp where empno = ?
DEBUG [main] - ==> Parameters: 7369(Integer)
TRACE [main] - <== Columns: EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO
TRACE [main] - <== Row: 7369, SMITH, CLERK, 7902.0, 1980-12-17, 800.0, null, 20
DEBUG [main] - <== Total: 1
Emp{empno=7369, ename='SMITH', job='CLERK', mgr=7902, hiredate=Wed Dec 17 08:00:00 CST 1980, sal=800.0, common=null, deptno=20}
但是个人推荐使用全路径,因为点击那个类也就是Emp的时候,就会自动跳转到Emp.class中。简写的不行。親看如下效果:別名的時候没办法点击。
- 包下有相同的类。
也就是包下不同路径下,有两个相同的User.class
此时若是还是使用上述批量默认别名的话,mybatis就会报错,说冲突,因为你有多个User.class。此时我们为了解决这个问题,我们可以使用注解。
此时一旦添加注解就不能使用默认的别名了,但是全类名还是可以使用的。
4.2 properties
当需要引入外部的配置文件的时候,可以使用这样的方式,类似于<context:property-placeholder location>
- resource:表示从当前项目的类路径中进行加载,如果用的是idea指的是resource资源目录下的配置文件
- url:可以从当前文件系统的磁盘目录查找配置,也可以从网络上的资源进行引入
<properties resource="db.properties"></properties>
这样就可以通过加载配置文件方式,来连接mysql数据库,而不是写在代码中,可以解耦。写在代码中的时候,变更mysql的时候会需要去改代码。mybatis结合spring的时候,这个标签可以不用写,都交给spring去管理了。
4.2 settings
- mapUnderscoreToCamelCase
开启驼峰标识验证,默认是false,即从经典数据库列名A_COLUMN到经典的Java属性名aColumn的类型映射。
数据库
mysql> desc user;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_name | varchar(255) | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
mysql> select * from user;
+----+-----------+
| id | user_name |
+----+-----------+
| 1 | zhangsan |
| 2 | zhangsan |
| 3 | lisi |
| 4 | lisi |
| 5 | lisi |
| 6 | lisi |
| 7 | lisi |
| 8 | lisi |
| 9 | lisi |
+----+-----------+
9 rows in set (0.00 sec)
而java类
package com.cmz.bean;
/**
* @author summer
* @create 2020-04-08 17:42
*/
public class User {
private Integer id;
private String userName;
public User() {
}
public User(Integer id, String userName) {
this.id = id;
this.userName = userName;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", userName='" + userName + '\'' +
'}';
}
}
从上面可以看出,mysql的字段user_name
和Java的属性userName不对应
,可以在没配置驼峰语法之前是这个字段是没办法显示的。
此时也可以通过alias显示。
但是我们可以通过开启驼峰语法,关联映射。
<settings>
<!--开启驼峰标识验证-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
4.3 typeHandlers
<typeHandlers>
<typeHandler handler="" ></typeHandler>
<package name=""/>
</typeHandlers>
4.4 objectFactory
<!--当需要自定义对象工厂的时候实现此标签,完成结果集到java对象实例化的过程-->
<objectFactory type=""></objectFactory>
4.5 plugins
mybatis分页器插件
https://pagehelper.github.io/
4.6 environments
https://mybatis.org/mybatis-3/zh/configuration.html#environments
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
<environments default="development">
<!--配置具体的环境属性
default: 可以选择指定的某种环境。
id:表示当前环境的名称
transactionManager: 事务管理器
type: 事务管理器的类型,JDBC|MANGED|自定义事务
-->
<!--测试环境-->
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type=""></dataSource>
</environment>
<!--开发环境-->
<environment id="development">
<!--事务管理器,每一种数据源都需要配置具体的事务管理器
type:表示事务管理器的类型
jdbc:使用jdbc原生的事务控制
managed:什么都没做
-->
<transactionManager type="JDBC"/>
<!--https://mybatis.org/mybatis-3/zh/configuration.html#environments-->
<!--配置具体的数据源的类型
type:表示数据源的类型
pooled:使用数据库连接池
unpooled:每次都打开和关闭一个链接
-->
<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>
4.7 databaseIdProvider
https://mybatis.org/mybatis-3/zh/configuration.html#databaseIdProvider
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId
属性。 MyBatis 会加载带有匹配当前数据库 databaseId
属性和所有不带 databaseId
属性的语句。 如果同时找到带有 databaseId
和不带 databaseId
的相同语句,则后者会被舍弃。 为支持多厂商特性,只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider
即可:
<databaseIdProvider type="DB_VENDOR" />
databaseIdProvider 对应的 DB_VENDOR 实现会将 databaseId 设置为 DatabaseMetaData#getDatabaseProductName()
返回的字符串。 由于通常情况下这些字符串都非常长,而且相同产品的不同版本会返回不同的值,你可能想通过设置属性别名来使其变短:
mybati_config.xml
<!--提供了不同的数据库厂商的标识,当有数据库移植的需求的时候,
可以根据不同的数据库来执行不同的sql语句,用来扩展数据库的移植性
-->
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="SQL Server" value="sqlserver"/>
<property name="Oracle" value="oracle"/>
</databaseIdProvider>
提供了属性别名时,databaseIdProvider 的 DB_VENDOR 实现会将 databaseId 设置为数据库产品名与属性中的名称第一个相匹配的值,如果没有匹配的属性,将会设置为 “null”。如下例子,我同一个查询,
- 使用以后使用mysql数据库的话,
EmpDao.xml
中的语句的databaseId="mysql"
- 若是以后想使用oracle的话,
EmpDao.xml
中的语句的databaseId="oracle"
EmpDao.xml
<select id="selectEmpByEmpno" resultType="com.cmz.bean.Emp" databaseId="mysql">
select * from emp where empno = #{empno}
</select>
<select id="selectEmpByEmpno" resultType="com.cmz.bean.Emp" databaseId="oracle">
select * from emp where empno = #{empno}
</select>
其中databaseId后面的取值一定是上面databaseIdProvider里面的配置的值,databaseId配置只能等于
mybati_config.xml
中之前配置好的databaseIdProvider的那几个值,否则报错。
不过蛋疼的事情是,一般不这么干。java始终还是比django差太TMD的大了。Django根本都不需要这样干,直接改配置文件就可以了,sql基本不写,都是ORM框架给你搞的,那个才是真正的ORM,你妹的假的ORM框架MyBatis。
4.8 mappers
是来将mapper映射文件引入到配置文件中,方便程序启动的时候进行加载
每次在进行填写的时候需要注意,写完xml映射之后一定要添加到mybatis-config文件中
注册配置文件
resource:从项目的类路径下加载对应的映射文件d
url: 从本地磁盘目录或者网络中引入映射文件
file:///var/mapper/authorMapper.xml
注册接口
class: 可以直接引入类的完全限定名,可以使用注解的方式进行使用,
1. 有sql映射,映射文件名必须和接口名同名,并且放在与接口同一个目录下
2. 没有sql映射,所有sql都是利用注解写在接口上。
3. 批量注解
推荐:
1. 比较重要复杂的Dao接口我们来写sql映射文件。
2. 不重要简单的Dao接口为了开发快速可以使用注解。
4.8.1 基于注解
使用注解测试,新建UserDaoAnnotation.class
package com.cmz.dao;
import com.cmz.bean.User;
import org.apache.ibatis.annotations.Select;
public interface UserDaoAnnotation {
@Select("select * from user where id = #{id}")
public User selectUserById(Integer id);
}
mybatis_config.xml
中配置
<mappers>
<!--<mapper resource="EmpDao.xml"/>-->
<!--<mapper resource="UserDao.xml"/>-->
<mapper class="com.cmz.dao.UserDaoAnnotation"></mapper>
</mappers>
测试类代码
/*查询*/
@Test
public void test06() throws IOException {
//获取与数据库相关的会话
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取对应的映射接口对象
UserDaoAnnotation userDaoAnnotation = sqlSession.getMapper(UserDaoAnnotation.class);
//执行对应的sql
User user = userDaoAnnotation.selectUserById(1);
System.out.println(user);
sqlSession.close();
}
运行结果
DEBUG [main] - ==> Preparing: select * from user where id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
TRACE [main] - <== Columns: id, user_name
TRACE [main] - <== Row: 1, zhangsan
DEBUG [main] - <== Total: 1
User{id=1, userName='zhangsan'}
java就tmd一样,贼乱。什么都是乱糟糟的。无奈java还是你大爷,生态太大了。
以上是我单独测试userDaoAnnotation注解方式。所以屏蔽了上面两个。
4.8.2 不基于注解
如果不想以注解的方式引入呢?如果想要class的方式引入配置文件,可以将xml文件添加到具体的类的同级目录下
方法1: 如果是maven的项目的话,需要添加如下配置,因为maven默认只会编译java文件,需要把xml文件也添加到指定目录中,把以下加入maven中
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
pom.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cmz</groupId>
<artifactId>mybatis_demo1</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
方法2: 在resource资源目录下,创建跟dao层一样的同级目录即可,将配置文件放到指定的目录
如果需要引入多个配置文件,可以直接定义包的名称
resource目录下配置的映射文件必须要具体相同的目录
mybatis_config.xml
<mappers>
<!--<mapper resource="EmpDao.xml"/>-->
<!--<mapper resource="UserDao.xml"/>-->
<!--<mapper class="com.cmz.dao.UserDaoAnnotation"></mapper>-->
<!--<mapper class="com.cmz.dao.UserDao"></mapper>-->
<!--如果需要引入多个配置文件,可以直接定义包的名称
resource目录下配置的映射文件必须要具体相同的目录
-->
<package name="com.cmz.dao"/>
</mappers>