《MyBatis的CRUD》
带着目标去学习,效果很不一样
学习目标
- 掌握sqlMapConfig.xml中常用标签
- 掌握mybatis框架在DAO层的开发
- 能够完成单表的CRUD操作
- 掌握mybatis框架的输入输出映射
回顾动态代理
代理模式的组成
作用:对真实角色功能的增强,真实角色和代理角色都是抽象角色的子类
- 抽象角色:定义了要实现的功能,通常使用接口。这个案例中就是UserMapper接口
- 真实角色:可以不存在的,这里没有
- 代理角色:也实现了接口中方法,使用动态代理来实现UserMapper接口,并且重写其中方法: findAllUsers()
动态代理的好处
- 接口的代理对象由程序在执行的过程中动态生成,不用我们自己去写一个类实现接口中所有的方法
- 可以动态生成任意接口的对象
Proxy类中的方法
InvocationHandler接口
模拟查询所有用户的方法
package com.itheima.utils;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* 访问数据库的工具类
*/
public class JdbcUtils {
//可以把几个字符串定义成常量:用户名,密码,URL,驱动类
private static final String USER = "root";
private static final String PWD = "root";
private static final String URL = "jdbc:mysql://localhost:3306/day24";
private static final String DRIVER = "com.mysql.jdbc.Driver";
/**
* 注册驱动,为了兼容1.5以前的版本
*/
static {
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 得到数据库的连接
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PWD);
}
/**
* 关闭连接
* 查询调用这个方法
*/
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 关闭连接
* 增删改没有结果集
*/
public static void close(Connection connection, Statement statement) {
//直接调用上面的方法
close(connection, statement, null);
}
/**
* 通用的增删改的方法
* @param sql 要执行的SQL语句
* @param params 替换占位符的真实地址,在方法内部是一个数组(从0开始)
* @return 影响的行数
*/
public static int update(String sql, Object... params) {
Connection connection = null;
PreparedStatement ps = null;
int row = 0; //影响的行数
try {
//1.创建连接对象
connection = getConnection();
//2.创建预编译的语句对象
ps = connection.prepareStatement(sql);
//2.5 得到参数元数据
ParameterMetaData metaData = ps.getParameterMetaData();
int count = metaData.getParameterCount(); //获取有几个参数
//3.替换占位符为真实的值
for (int i = 0; i < count; i++) {
ps.setObject(i + 1, params[i]); //一定是个对象类型
}
//4.执行SQL语句
row = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(connection, ps); //调用自己的方法
}
return row;
}
/**
* 通用的查询方法
* @param sql 要执行的SQL语句
* @param clazz 要实例化的类型,如:Student.class, Employee.class
* @param params 替换占位符的真实值
* @return 封装好的集合对象
*/
public static <T> List<T> query (String sql, Class<T> clazz, Object...params) {
Connection connection = null;
PreparedStatement ps = null;
ResultSet resultSet = null;
List<T> list = new ArrayList<>();
try {
//1.创建连接对象
connection = getConnection();
//2. 创建预编译的语句对象
ps = connection.prepareStatement(sql);
//2.5 得到参数元数据
ParameterMetaData metaData = ps.getParameterMetaData();
int count = metaData.getParameterCount(); //获取有几个参数
//3.替换占位符为真实的值
for (int i = 0; i < count; i++) {
ps.setObject(i + 1, params[i]); //一定是个对象类型
}
//4.执行查询操作
resultSet = ps.executeQuery();
//5.遍历整个结果集,封装到集合中,每个元素是一个对象
while(resultSet.next()) {
//每条记录封装成一个对象,创建一个对象
T obj = clazz.getConstructor().newInstance();
//先得到实体类中有哪些属性
Field[] fields = clazz.getDeclaredFields(); //得到所有成员变量,包含私有的
//遍历每个成员变量,进行赋值
for (Field field : fields) {
//私有的要暴力
field.setAccessible(true);
//列名=属性名
String name = field.getName();
//要赋值的对象,值
field.set(obj, resultSet.getObject(name)); //从结果集中获取数据
}
//6.添加到集合中
list.add(obj);
}
}
catch (Exception ex) {
ex.printStackTrace();
}
finally {
close(connection, ps, resultSet);
}
return list;
}
}
package com.itheima.framework;
import com.itheima.entity.User;
import com.itheima.utils.JdbcUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
/**
* 模拟SqlSession类
*/
public class SqlSession {
/**
* 获取UserMapper的代理对象
*/
public <T> T getMapper(Class<T> daoInterface) {
/**
* 生成T接口的代理对象
* 参数1:类加载器
* 参数2:所有被代理的接口数组,这里就是一个。创建一个匿名数组
* new String[] {"a","b","c"}