mybatis的使用及源码分析(一) mybatis介绍以及原生Mybatis项目搭建
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
mybatis github官方地址:https://github.com/mybatis/mybatis-3
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
ORM(Object/Relational Mapping)对象关系映射。是一种数据持久化的技术。在对象模型和关系型数据库之间建立关系,并且提供了一种机制,通过JavaBean对象去操作数据库表中的数据。MyBatis通过简单的XML或者注解进行配置和原始映射,将实体类和SQL语句之间建立映射关系,是一种半自动化的ORM实现。
这次的搭建不使用Spring或基于Mybatis的封装(如Mybatis-Plus)等,只使用原生Mybatis配置,更易于对于框架的了解。构建工具采用了Maven、开发工具采用IDEA、数据库采用Mysql
此次搭建项目github地址:https://github.com/zhuquanwen/mybatis-learn/releases/tag/one-base
下面介绍第一个Mybatis项目搭建:
1、项目总体结构
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.learn.zqw</groupId>
<artifactId>mybatis-learn</artifactId>
<version>1.0-SNAPSHOT</version>
<repositories>
<repository>
<id>central</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<layout>default</layout>
<!-- 是否开启发布版构件下载 -->
<releases>
<enabled>true</enabled>
</releases>
<!-- 是否开启快照版构件下载 -->
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source> <!-- 源代码使用的JDK版本 -->
<target>1.8</target> <!-- 需要生成的目标class文件的编译版本 -->
<encoding>UTF-8</encoding><!-- 字符集编码 -->
<skip>true</skip><!-- 跳过测试 -->
<verbose>true</verbose>
<showWarnings>true</showWarnings>
<fork>true</fork><!-- 要使compilerVersion标签生效,还需要将fork设为true,用于明确表示编译版本配置的可用 -->
<executable><!-- path-to-javac --></executable><!-- 使用指定的javac命令,例如:<executable>${JAVA_1_4_HOME}/bin/javac</executable> -->
<compilerVersion>1.3</compilerVersion><!-- 指定插件将使用的编译器的版本 -->
<meminitial>128m</meminitial><!-- 编译器使用的初始内存 -->
<maxmem>512m</maxmem><!-- 编译器使用的最大内存 -->
<!-- <compilerArgument>-verbose -bootclasspath ${java.home}\lib\rt.jar</compilerArgument>--><!-- 这个选项用来传递编译器自身不包含但是却支持的参数选项 -->
</configuration>
</plugin>
</plugins>
</build>
</project>
使用maven-compiler-plugin
插件配置了一些基本配置,包括JDK版本、编码方式等
在repository
下配置了阿里云MAVEN镜像加快依赖拉取速度
除了日志相关依赖,引入junit
做单元测试,引入mysql
做mysql驱动连接,引入lombok
做自动get、set方法注入,最后引入本文最重要的依赖org.mybatis
3、user.sql 介绍user.sql
为本文测试用的Sql脚本,可自行导入Mysql
/*
Navicat MySQL Data Transfer
Source Server : localhost
Source Server Version : 50722
Source Host : localhost:3306
Source Database : mybatis_learn
Target Server Type : MYSQL
Target Server Version : 50722
File Encoding : 65001
Date: 2020-03-06 21:51:56
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', '张三', '123456');
INSERT INTO `user` VALUES ('2', '李四', '123456');
INSERT INTO `user` VALUES ('3', '王五', '123456');
4、实体类
domain包下定义了数据库对应的实体类User
package com.learn.zqw.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
5、mapper.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.learn.zqw.mapper.IUserMapper">
<resultMap id="BaseResultMap" type="com.learn.zqw.domain.User">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
</resultMap>
<sql id="Base_Column_List">
id, username, password
</sql>
<!--此处配置dao接口所要实现的SQL语句-->
<!--插入一条记录-->
<insert id="insert" parameterType="com.learn.zqw.domain.User">
insert into user (username, password) values (#{username}, #{password})
</insert>
<!--插入记录并返回ID-->
<insert id="insertAndGetId" useGeneratedKeys="true" keyProperty="id" keyColumn="id" parameterType="com.learn.zqw.domain.User">
insert into user(username, password)
values(#{username}, #{password})
</insert>
<!--插入记录并返回ID第二种方式-->
<insert id="insertAndGetId2" parameterType="com.learn.zqw.domain.User" >
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
select LAST_INSERT_ID()
</selectKey>
insert into user(username, password)
values(#{username}, #{password})
</insert>
<!--条件插入-->
<insert id="insertSelective" parameterType="com.learn.zqw.domain.User">
insert into user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username != null and username != ''">username,</if>
<if test="password != null and password != ''">username,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="username != null and username != ''">#{username},</if>
<if test="password != null and password != ''">#{username},</if>
</trim>
</insert>
<!--条件修改-->
<update id="updateByPrimaryKeySelective" parameterType="com.learn.zqw.domain.User">
update user
<set>
<if test="username != null and username != ''">
username = #{username},
</if>
<if test="password != null and password != ''">
password = #{password},
</if>
</set>
where ID = #{id}
</update>
<!--修改-->
<update id="updateByPrimaryKey" parameterType="com.learn.zqw.domain.User">
update user set username = #{username}, password = #{password} where id = #{id}
</update>
<!--查询所有-->
<select id="findAll" resultType="com.learn.zqw.domain.User">
select * from user
</select>
<!--按照ID查询-->
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer">
select
<include refid="Base_Column_List"/>
from user where id = #{id}
</select>
<!--按照ID删除-->
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from user where id = #{id}
</delete>
</mapper>
这里定义里各类查询Sql,包括:查询、新增、能够返回ID的新增、按条件新增、删除、修改、按条件修改等。namespace
对应mapper层的接口,parameterType
对应实体层的实体User
6、mapper层
package com.learn.zqw.mapper;
import com.learn.zqw.domain.User;
import java.util.List;
public interface IUserMapper {
List<User> findAll();
int insert(User user);
Integer insertAndGetId(User user);
Integer insertAndGetId2(User user);
int insertSelective(User user);
int updateByPrimaryKeySelective(User user);
int updateByPrimaryKey(User user);
User selectByPrimaryKey(Integer id);
int deleteByPrimaryKey(Integer id);
}
接口内定义了与IUserMapper.xml中对应的函数。
7、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">
<!--mybatis的主配置文件-->
<configuration>
<!--配置环境-->
<environments default="mysql">
<!--配置mysql环境-->
<environment id="mysql">
<!--配置事务类型-->
<transactionManager type="JDBC" />
<!--配置数据源(连接池)-->
<dataSource type="POOLED">
<!--配置连接数据库的4个基本信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_learn?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
<mappers>
<mapper resource="com/learn/zqw/IUserMapper.xml" />
</mappers>
</configuration>
这里transactionManager
定义了事务类型,POOLED
为Mybatis内置的连接池,dataSource
中定义了数据库连接信息,注意url
中最好加上编码,不然可能会出现中文乱码问题,mappers
标签定义了定义Mapper的XML的位置。
8、单元测试
package com.learn.zqw;
import com.learn.zqw.domain.User;
import com.learn.zqw.mapper.IUserMapper;
import lombok.Cleanup;
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.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
@RunWith(JUnit4.class)
public class BaseTest {
/**
* 测试基本Mybatise的运行
* */
@Test
public void baseTest() throws IOException {
//1.读取配置文件
@Cleanup InputStream in = Resources.getResourceAsStream ("SqlMapConfig.xml");
//2. 创建SqlSessionFactory
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产SqlSession对象
@Cleanup SqlSession session = factory.openSession ();
//4.使用SqlSession创建Mapper接口的代理对象
IUserMapper userMapper = session.getMapper(IUserMapper.class);
//5.使用代理对象执行分法
//1)获取所有数据
List<User> users = userMapper.findAll();
Assert.assertNotNull(users);
System.out.println("=========查询所有的结果======");
if (users != null) {
users.forEach(System.out::println);
}
System.out.println("=========查询所有的结果End======");
//2)插入记录
int insertResult = userMapper.insert(new User(UUID.randomUUID().toString(), "123456"));
Assert.assertEquals(1, insertResult);
//3)插入记录返回ID
User user1 = new User(UUID.randomUUID().toString(), "123456");
int getId = userMapper.insertAndGetId(user1);
System.out.printf("插入并返回ID:[%d]\n", user1.getId());
Assert.assertNotNull(user1.getId());
//4)插入记录返回ID2
User user2 = new User(UUID.randomUUID().toString(), "123456");
int getId2 = userMapper.insertAndGetId2(user2);
System.out.printf("插入并返回ID:[%d]\n", user2.getId());
Assert.assertNotNull(user2.getId());
//5)按条件插入
User user3 = new User();
user3.setUsername(UUID.randomUUID().toString());
int selectiveInsertId = userMapper.insertSelective(user3);
Assert.assertEquals(1, selectiveInsertId);
Assert.assertNull(user3.getPassword());
//6)按ID查询
User user = userMapper.selectByPrimaryKey(1);
Assert.assertNotNull(user);
//7)按条件修改
user.setUsername(null);
user.setPassword("1234567");
int x = userMapper.updateByPrimaryKeySelective(user);
Assert.assertEquals(1, x);
//8)全部修改
User user4 = userMapper.selectByPrimaryKey(1);
user4.setPassword("12345678");
int y = userMapper.updateByPrimaryKey(user4);
Assert.assertEquals(1, y);
session.commit();
//9)删除,先删了再回滚下
int i = userMapper.deleteByPrimaryKey(1);
Assert.assertEquals(1, i);
session.rollback();
}
}