1、接口层-和数据库交互的方式
MyBatis和数据库的交互有两种方式:
使用传统的MyBatis提供的API;
使用Mapper接口;
2、使用Mapper接口
MyBatis 将配置文件中的每一个<mapper> 节点抽象为一个 Mapper 接口:
这个接口中声明的方法和<mapper> 节点中的<select|update|delete|insert> 节点项对应,即<select|update|delete|insert> 节点的id值为Mapper 接口中的方法名称,
parameterType 值表示Mapper 对应方法的入参类型,而resultMap 值则对应了Mapper 接口表示的返回值类型或者返回结果集的元素类型。
根据MyBatis 的配置规范配置好后,通过SqlSession.getMapper(XXXMapper.class)方法,MyBatis 会根据相应的接口声明的方法信息,通过动态代理机制生成一个Mapper 实例,
我们使用Mapper接口的某一个方法时,MyBatis会根据这个方法的方法名和参数类型,确定Statement Id,底层还是通过SqlSession.select("statementId",parameterObject);或者SqlSession.update("statementId",parameterObject); 等等来实现对数据库的操作,MyBatis引用Mapper 接口这种调用方式,纯粹是为了满足面向接口编程的需要。
(其实还有一个原因是在于,面向接口的编程,使得用户在接口上可以使用注解来配置SQL语句,这样就可以脱离XML配置文件,实现“0配置”)。
3、数据处理层
数据处理层可以说是MyBatis的核心,从大的方面上讲,它要完成两个功能:
通过传入参数构建动态SQL语句;
SQL语句的执行以及封装查询结果集成List<E>;
4、主要构件及其相互关系
从MyBatis代码实现的角度来看,MyBatis的主要的核心部件有以下几个:
SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能;
Executor:MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护;
StatementHandler:封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。
ParameterHandler:负责对用户传递的参数转换成JDBC Statement 所需要的参数;
ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
TypeHandler:负责java数据类型和jdbc数据类型之间的映射和转换;
MappedStatement:MappedStatement维护了一条<select|update|delete|insert>节点的封装;
SqlSource:负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回;
BoundSql:表示动态生成的SQL语句以及相应的参数信息;
Configuration:MyBatis所有的配置信息都维持在Configuration对象之中;
5、纯手写Mybatis代码
思想:自定义注解+反射+动态代理实现
添加依赖包
<dependencies>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8</version>
</dependency>
</dependencies>
定义JDBCUtils:
package com.yehui.utils; import sun.security.krb5.Config; import java.sql.*;
import java.util.List; public class JDBCUtils {
private static String connect;
private static String driverClassName;
private static String URL;
private static String username;
private static String password;
private static boolean autoCommit; private static Connection conn;
private static PreparedStatement pre;
static {
Config();
}
private static void Config(){
/*
* 获取驱动
*/
driverClassName = "com.mysql.jdbc.Driver";
/*
* 获取URL
*/
URL = "jdbc:mysql://localhost:3306/study?useUnicode=true&characterEncoding=utf8";
/*
* 获取用户名
*/
username = "root";
/*
* 获取密码
*/
password = "root";
/*
* 设置是否自动提交,一般为false不用改
*/
autoCommit = false;
}
/**
* 载入数据库驱动类
*/
private static boolean load(){
try {
Class.forName(driverClassName);
return true;
} catch (ClassNotFoundException e) {
System.out.println("驱动类 " + driverClassName + " 加载失败");
e.printStackTrace();
}
return false;
}
/**
* 专门检查缓存的连接是否不可以被使用 ,不可以被使用的话,就返回 true
*/
private static boolean invalid() {
if (conn != null) {
try {
if (conn.isClosed() || !conn.isValid(3)) {
return true;
/*
* isValid方法是判断Connection是否有效,如果连接尚未关闭并且仍然有效,则返回 true
*/
}
} catch (SQLException e) {
e.printStackTrace();
}
/*
* conn 既不是 null 且也没有关闭 ,且 isValid 返回 true,说明是可以使用的 ( 返回 false )
*/
return false;
} else {
return true;
}
}
/**
* 建立数据库连接
*/
public static Connection connect() {
if (invalid()) { /* invalid为true时,说明连接是失败的 */
/* 加载驱动 */
load();
try {
/* 建立连接 */
conn = DriverManager.getConnection(URL, username, password);
} catch (SQLException e) {
System.out.println("建立 " + connect + " 数据库连接失败 , " + e.getMessage());
}
}
return conn;
}
/**
* 设置是否自动提交事务
**/
public static void transaction() { try {
conn.setAutoCommit(autoCommit);
} catch (SQLException e) {
System.out.println("设置事务的提交方式为 : " + (autoCommit ? "自动提交" : "手动提交") + " 时失败: " + e.getMessage());
}
} /** 提交事务 */
private static void commit(Connection c) {
if (c != null && !autoCommit) {
try {
c.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
} /** 回滚事务 */
private static void rollback(Connection c) {
if (c != null && !autoCommit) {
try {
c.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
} /**
* 释放资源
**/
public static void release(Object cloaseable) { if (cloaseable != null) { if (cloaseable instanceof ResultSet) {
ResultSet rs = (ResultSet) cloaseable;
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
} if (cloaseable instanceof Statement) {
Statement st = (Statement) cloaseable;
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
} if (cloaseable instanceof Connection) {
Connection c = (Connection) cloaseable;
try {
c.close();
} catch (SQLException e) {
e.printStackTrace();
}
} } }
/**
* 根据给定的带参数占位符的SQL语句,创建 PreparedStatement 对象
*
* @param SQL
* 带参数占位符的SQL语句
* @return 返回相应的 PreparedStatement 对象
*/
private static PreparedStatement prepare(String SQL, boolean autoGeneratedKeys) { PreparedStatement ps = null;
connect();
/* 如果连接是无效的就重新连接 */
transaction();
/* 设置事务的提交方式 */
try {
if (autoGeneratedKeys) {
ps = conn.prepareStatement(SQL, Statement.RETURN_GENERATED_KEYS);
} else {
ps = conn.prepareStatement(SQL);
}
} catch (SQLException e) {
System.out.println("创建 PreparedStatement 对象失败: " + e.getMessage());
} return ps; }
/*
*
* @param SQL 需要执行的 INSERT 语句
*
* @param autoGeneratedKeys 指示是否需要返回由数据库产生的键
*
* @param params 将要执行的SQL语句中包含的参数占位符的 参数值
*
* @return 如果指定 autoGeneratedKeys 为 true 则返回由数据库产生的键; 如果指定 autoGeneratedKeys
* 为 false 则返回受当前SQL影响的记录数目
*/
public static Object insert(String SQL, List<?> params,boolean autoGeneratedKeys) {
//获得连接
conn = connect();
int var = 0;
try {
if (SQL == null || SQL.trim().isEmpty()) {
throw new RuntimeException("你没有指定SQL语句,请检查是否指定了需要执行的SQL语句");
}
// 如果不是 insert 开头开头的语句
if (!SQL.trim().toLowerCase().startsWith("insert")) {
System.out.println(SQL.toLowerCase());
throw new RuntimeException("你指定的SQL语句不是插入语句,请检查你的SQL语句");
}
if (params!=null&¶ms.size()>0) {//有参数的情况
//创建连接对象
pre = prepare(SQL,autoGeneratedKeys);
for (int i = 0; i <params.size(); i++) {
pre.setObject(i+1,typeof(params.get(i)));
}
int count = pre.executeUpdate();
if (autoGeneratedKeys) {// 如果希望获得数据库产生的键
ResultSet rs = pre.getGeneratedKeys(); // 获得数据库产生的键集
if (rs.next()) { // 因为是保存的是单条记录,因此至多返回一个键
var = rs.getInt(1); // 获得值并赋值给 var 变量
}
}else{
var = count;// 如果不需要获得,则将受SQL影像的记录数赋值给 var 变量
}
commit(conn);
}else{// 说明没有参数
Statement st = statement();
Connection connection = st.getConnection();
int count = st.executeUpdate(SQL);
if (autoGeneratedKeys) { // 如果企望获得数据库产生的键
ResultSet rs = st.getGeneratedKeys(); // 获得数据库产生的键集
if (rs.next()) { // 因为是保存的是单条记录,因此至多返回一个键
var = rs.getInt(1); // 获得值并赋值给 var 变量
}
} else {
var = count; // 如果不需要获得,则将受SQL影像的记录数赋值给 var 变量
}
commit(conn); // 提交事务
}
} catch (Exception e) {
System.out.println("数据保存失败: " + e.getMessage());
rollback(conn);
}
return var;
} /**
* 创建 Statement 对象
*/
public static Statement statement() {
Statement st = null;
connect();
/* 如果连接是无效的就重新连接 */
transaction();
/* 设置事务的提交方式 */
try {
st = conn.createStatement();
} catch (SQLException e) {
System.out.println("创建 Statement 对象失败: " + e.getMessage());
} return st;
} /**
* 类型转换
* @param o
* @return
*/
private static Object typeof(Object o) {
Object r = o; if (o instanceof java.sql.Timestamp) {
return r;
}
// 将 java.util.Date 转成 java.sql.Date
if (o instanceof java.util.Date) {
java.util.Date d = (java.util.Date) o;
r = new java.sql.Date(d.getTime());
return r;
}
// 将 Character 或 char 变成 String
if (o instanceof Character || o.getClass() == char.class) {
r = String.valueOf(o);
return r;
}
return r;
} /**
* 得到查询结
* @param newSql
* @param parameValues
* @return
*/
public static ResultSet select(String newSql, List<Object> parameValues) {
ResultSet rs = null;
//获得连接
conn = connect();
if (newSql == null || newSql.trim().isEmpty() || !newSql.trim().toLowerCase().startsWith("select")) {
throw new RuntimeException("你的SQL语句为空或不是查询语句");
}
try {
if (parameValues!=null&¶meValues.size()>0) {//这是有参数的情况下
//获取连接对象
pre = conn.prepareStatement(newSql);
for (int i = 0; i < parameValues.size(); i++) {
pre.setObject(i+1,parameValues.get(i));
}
rs = pre.executeQuery();
}else{
/* 说明没有传入任何参数 */
Statement st = statement();
rs = st.executeQuery(newSql); // 直接执行不带参数的 SQL 语句
}
} catch (Exception e) {
System.out.println("执行SQL失败: " + e.getMessage());
}
return rs;
}
}
定义SQLUtils
package com.yehui.utils; import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; public class SQLUtils { /**
* 得到插入sql需要传递的参数
* @param insertSql
*/
public static List<String> getInsertSqlParams(String insertSql){
Integer startIndex = insertSql.indexOf("values");
insertSql = insertSql.substring(startIndex+"values".length())
.replace("#{","")
.replace("}","")
.replace("(","")
.replace(")","");
return Arrays.asList(insertSql.split(","));
}
/**
* 得到查询sql需要传递的参数
* @param selectSql
*/
public static List<String> getSelectSqlParams(String selectSql){
Integer startIndex = selectSql.indexOf("where");
selectSql = selectSql.substring(startIndex+"where".length());
String[] ands = selectSql.split("and");
List<String> list = new ArrayList<>();
for (String and : ands) {
String[] split = and.split("=");
String replace = split[1].replace("#{", "").replace("}", "").trim();
list.add(replace);
}
return list;
}
/**
* 将sql参数转换为?
* @param sql
* @param params
* @return
*/
public static String covertSql(String sql,List<String> params){
for (String param : params) {
sql = sql.replace("#{"+param+"}","?");
}
return sql;
} public static void main(String[] args) {
//insert into account(username,money) values(#{username},#{money})
//select * from account where username=#{username} and money=#{money}
List<String> p = new ArrayList<>();
p.add("username");
p.add("money");
getSelectSqlParams("select * from account where username=#{username} and money=#{money}");
}
}
定义一个mapper接口
package com.yehui.mapper; import com.yehui.annotation.ExtInsert;
import com.yehui.annotation.ExtParam;
import com.yehui.annotation.ExtSelect;
import com.yehui.entity.User; public interface UserMapper {
// 1.接口既然不能被实例化?那么我们是怎么实现能够调用的?
// 2.参数如何和sql参数绑定
// 3.返回结果
@ExtSelect("select * from account where username=#{username} and money=#{money} ")
User selectUser(@ExtParam("username") String username, @ExtParam("money") Integer money); @ExtInsert("insert into account(username,money) values(#{username},#{money})")
int insertUser(@ExtParam("username") String username, @ExtParam("money") Integer money);
}
定义注解
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtInsert { String value();
} // Mapper映射注解
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtMapper { } @Target(value = { ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtParam { String value(); } // 查询注解
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtSelect { String value(); }
定义动态代理类
package com.yehui; import com.sun.xml.internal.ws.api.model.MEP;
import com.yehui.annotation.ExtInsert;
import com.yehui.annotation.ExtParam;
import com.yehui.annotation.ExtSelect;
import com.yehui.utils.JDBCUtils;
import com.yehui.utils.SQLUtils; import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap; public class MyInvocationHandlerMbatis implements InvocationHandler {
/**
* 这个就是我们要代理的真实对象
*/
private Object target; /**
* 构造方法,给我们要代理的真实对象赋初值
*
* @param target
*/
public MyInvocationHandlerMbatis(Object target) {
this.target = target;
}
/**
* 该方法负责集中处理动态代理类上的所有方法调用。 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
*
* @param proxy
* 代理类实例
* @param method
* 被调用的方法对象
* @param args
* 调用参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取注解
if(method.isAnnotationPresent(ExtInsert.class)){
ExtInsert extInsert = method.getAnnotation(ExtInsert.class);
//得到注解上面的sql
String insertSql = extInsert.value();
//得到参数列表
ConcurrentHashMap<String,Object> paramMap = getExtParams(method,args);
//得到注解上面的参数
List<String> params = SQLUtils.getInsertSqlParams(insertSql);
//转换sql 替换为?
String newSql = SQLUtils.covertSql(insertSql, params);
//得到参数值
List<Object> parameValues = new ArrayList<>();
for (String parame : params) {
parameValues.add(paramMap.get(parame));
}
//执行sql语句
return JDBCUtils.insert(newSql,parameValues,false);
}
//得到查询注解
if(method.isAnnotationPresent(ExtSelect.class)){
//得到sql上面的注解
ExtSelect extSelect = method.getAnnotation(ExtSelect.class);
//得到注解上的值
String selectSql = extSelect.value();
//得到参数列表
ConcurrentHashMap<String,Object> paramMap = getExtParams(method,args);
//得到注解上面的参数
List<String> params = SQLUtils.getSelectSqlParams(selectSql);
//转换sql 替换为?
String newSql = SQLUtils.covertSql(selectSql, params);
//得到参数值
List<Object> parameValues = new ArrayList<>();
for (String parame : params) {
parameValues.add(paramMap.get(parame));
}
//执行sql得到查询结果
ResultSet rs = JDBCUtils.select(newSql,parameValues);
if (!rs.next()) {
// 没有查找数据
return null;
}
//得到返回值类型
Class<?> returnType = method.getReturnType();
// 向上移动
rs.previous();
//得到实例化对象
Object obj = returnType.newInstance();
while (rs.next()){
for(String parm:params){
//得到对应的属性
Field declaredField = returnType.getDeclaredField(parm);
declaredField.setAccessible(true);
// 赋值参数
declaredField.set(obj,rs.getObject(parm));
}
}
return obj;
}
return null;
} /**
* 将参数放入map中
*/
public static ConcurrentHashMap<String,Object> getExtParams(Method method,Object[] args){
Parameter[] parameters = method.getParameters();
ConcurrentHashMap<String,Object> paramMap = new ConcurrentHashMap<>();
for (int i = 0; i < parameters.length; i++) {
// 参数信息
Parameter parameter = parameters[i];
if(parameter.isAnnotationPresent(ExtParam.class)){
ExtParam extParam = parameter.getAnnotation(ExtParam.class);
// 参数名称
String value = extParam.value();
//args[i] 参数值
paramMap.put(value,args[i]);
}
}
return paramMap;
}
}
创建SqlSession
public class SqlSession { public static <T> T getMapper(Class<T> tClass){
return (T) Proxy.newProxyInstance(tClass.getClassLoader(),new Class[]{tClass},
new MyInvocationHandlerMbatis(tClass));
}
}
创建一个实体
package com.yehui.entity; public class User {
private String username;
private Integer money;
private Integer id; public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public Integer getMoney() {
return money;
} public void setMoney(Integer money) {
this.money = money;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} @Override
public String toString() {
return "User{" + "username='" + username + '\'' + ", money=" + money + ", id=" + id + '}';
}
}
测试查询:
UserMapper mapper = SqlSession.getMapper(UserMapper.class);
User jack = mapper.selectUser("jack", 6000);
System.out.println(jack);
测试新增:
UserMapper mapper = SqlSession.getMapper(UserMapper.class);
int count = mapper.insertUser("user111", 1000);
System.out.println(count);
网站:https://blog.csdn.net/kuailebuzhidao/article/details/88355236