package src.orm;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 这是自己定义的一个规则
* 提供策略的规则接口
*/
public interface RowMapper {
//设计一个规则方法
//将rs的信息组装成一个对象domain
//是否需要参数 rs
//是否需要返回值 <T>
<T>T mapperRow(ResultSet rs) throws SQLException;
}
package src.orm;
import orm.SQLAndKey;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Handler {
//============================
//设计一个小弟方法---帮助我们解析SQL语句
SQLAndKey parseSQL(String sql){
//解析之前肯定需要两个变量
StringBuilder newSQL = new StringBuilder();
List<String> keyList = new ArrayList<>();
//解析SQL
//insert into student values(#{sid},#{sname},#{ssex},#{sage})
while(true){
//按照规定的结构来找寻索引位置
int left = sql.indexOf("#{");
int right = sql.indexOf("}");
//判断两个索引的位置是否合法
if(left!=-1 && right!=-1 && left<right){
//找到一组#{key}
newSQL.append(sql.substring(0,left));//截取左边的部分拼接到newSQL里
newSQL.append("?");
keyList.add(sql.substring(left+2,right));
}else{//找不到
newSQL.append(sql);
break;
}
sql = sql.substring(right+1);
}
return new SQLAndKey(newSQL,keyList);
}
//----------------------------
//设计一个小小弟--负责帮下面那个handlerParameter方法处理map集合的拼接
private void setMap(PreparedStatement pstat, Object obj, List<String> keyList) throws SQLException {
//按照keyList规定的顺序 从map中获取元素 让pstat拼接到SQL上
//还原一下obj类型
Map map = (Map)obj;
for(int i=0;i<keyList.size();i++){
pstat.setObject(i+1,map.get(keyList.get(i)));
}
}
//设计一个小小弟--负责帮下面那个handlerParameter方法处理domain的拼接
private void setDomain(PreparedStatement pstat,Object obj,List<String> keyList) throws SQLException, NoSuchFieldException, IllegalAccessException {
//获取obj类型
Class clazz = obj.getClass();
//遍历keyList 规定了顺序
for(int i=0;i<keyList.size();i++){
String key = keyList.get(i);
//去domain对象中获取key对应属性的值
Field field = clazz.getDeclaredField(key);
//直接操作私有属性啦
field.setAccessible(true);
//拼接
// Object obj = 对象; field = clazz.getField();
// value = obj.getName(); value = field.get(对象);
pstat.setObject(i+1,field.get(obj));
}
}
//设计一个小弟方法---帮我们处理参数(SQL和对象中的值拼接在一起)
// 参数pstat Object 顺序? keyList<key>
void handlerParameter(PreparedStatement pstat,Object obj,List<String> keyList) throws SQLException {
//获取这个obj对象的Class
Class clazz = obj.getClass();
//clazz通常可以是什么类型
// 1.基础类型 int-Integer float-Float String
// 2.domain类型 Student Atm Teacher
// 3.map类型
if(clazz==int.class || clazz==Integer.class){
pstat.setInt(1,(Integer)obj);
}else if(clazz==float.class || clazz==Float.class){
pstat.setFloat(1,(Float)obj);
}else if(clazz==double.class || clazz==Double.class){
pstat.setDouble(1,(Double)obj);
}else if(clazz==String.class){
pstat.setString(1,(String)obj);
}else if(clazz.isArray()){
//自己处理 数组循环 对不起我不支持
}else{
//认为只剩下两个可能
//1.map
if(obj instanceof Map){
this.setMap(pstat,obj,keyList);
}else{//domain
try {
this.setDomain(pstat,obj,keyList);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
//----------------------------
//设计一个小小弟--负责帮下面那个handlerResult方法处理map返回值
private Map getMap(ResultSet rs) throws SQLException {
//创建map
Map result = new HashMap<String,Object>();
//获取rs中的全部信息(表格原来的列名 和 查询到的值) Set<Map<列名,值>>
ResultSetMetaData resultSetMetaData = rs.getMetaData();
//遍历含有列的信息
for(int i=1;i<=resultSetMetaData.getColumnCount();i++){
//获取一个列名字
String columnName = resultSetMetaData.getColumnName(i);
//根据列名字获取rs中的值
Object value = rs.getObject(columnName);
result.put(columnName,value);
}
return result;
}
//设计一个小小弟--负责帮下面那个handlerResult方法处理domain返回值
private Object getDomain(ResultSet rs,Class resultType) throws IllegalAccessException, InstantiationException, SQLException, NoSuchFieldException {
Object obj = null;
//通过Class反射创建对象
obj = resultType.newInstance();
//遍历rs
ResultSetMetaData resultSetMetaData = rs.getMetaData();
for(int i=1;i<=resultSetMetaData.getColumnCount();i++){
//获取一个列名字
String columnName = resultSetMetaData.getColumnName(i);
//反射找domain对象中对应列名的哪个属性
Field field =resultType.getDeclaredField(columnName);
//操作私有属性
field.setAccessible(true);
field.set(obj,rs.getObject(columnName));
}
return obj;
}
//设计一个小弟方法---帮我们处理返回值(rs信息取出 组装成一个对象 基础类型 domain map)
Object handlerResult(ResultSet rs,Class resultType) throws SQLException {
Object result = null;//变量用来存储最终的返回值
if(resultType==int.class || resultType==Integer.class){
result = rs.getInt(1);
}else if(resultType==float.class || resultType==Float.class){
result = rs.getFloat(1);
}else if(resultType==double.class || resultType==Double.class){
result = rs.getDouble(1);
}else if(resultType==String.class){
result = rs.getString(1);
}else{
//在这认为是对象 map domain
if(resultType==Map.class){
result = this.getMap(rs);
}else{
try {
result = this.getDomain(rs,resultType);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
return result;
}
//============================
}
package src.orm;
import java.util.ArrayList;
import java.util.List;
/**
* 这是我们自己定义的类
* 是为了解析带有#{key}形式的SQL时候
* 装载解析后的返回值的
* 解析后的返回值有两个
* 一个是带有问号形式的原来SQL
* 一个是ArrayList集合 集合内存放好多key
*/
public class SQLAndKey {
//属性 存放原先形式的sql 带问号的
private StringBuilder sql = new StringBuilder();
//属性 存放解析出来的那些#{key}
private List<String> keyList = new ArrayList();
//带参数的构造方法
public SQLAndKey(StringBuilder sql,List<String> keyList){
this.sql = sql;
this.keyList = keyList;
}
public String getSQL(){
return this.sql.toString();
}
public List<String> getKeyList(){
return this.keyList;
}
}
package src.orm;
import orm.Handler;
import orm.RowMapper;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 单独设计了一个类
* 负责帮所有的DAO来做事
* 以后只能在这个类的方法中见到JDBC的流程啦
* 以后所有的DAO再也看不见JDBC啦
* 以后的DAO只保留了一条SQL语句
*/
@SuppressWarnings("all")
public class SqlSession {
private orm.Handler handler = new Handler();
//面向配置文件开发
//驱动类 账号 密码都存在文件中
//单独设计一个小弟方法
//负责处理所有的增删改操作
// 参数 SQL , SQL语句上需要的那些问号值
// 返回值? void int--->行数 原本对象形式的容器--->数组Object[] 集合
// insert into student values(?,?,?,?)
// values->Object[] {10,"zzt","男",18}
public void update(String sql,Object... values){//容器目的存储好几个问号值的
String className = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/newtest?useSSL=false&characterEncoding=utf8";
String username = "root";
String password = "123456";
try {
//加载驱动
Class.forName(className);
//获取连接
Connection conn = DriverManager.getConnection(url,username,password);
//创建状态参数----预处理
PreparedStatement pstat = conn.prepareStatement(sql);
//给SQL问号赋值的过程
// 赋值 几个 什么类型
for(int i=0;i<values.length;i++){
pstat.setObject(i+1,values[i]);
}
//执行操作啦
pstat.executeUpdate();
//关闭
pstat.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//单独设计一个小弟方法
//负责处理所有DAO的单条查询
// 参数 SQL Object[]
// 返回值 <T> Object(Student Atm)
public <T>T selectOne(String sql, orm.RowMapper rm, Object... values){
Object obj = null;
String className = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/newtest?useSSL=false&characterEncoding=utf8";
String username = "root";
String password = "123456";
try {
//加载驱动
Class.forName(className);
//获取连接
Connection conn = DriverManager.getConnection(url,username,password);
//创建状态参数----预处理
PreparedStatement pstat = conn.prepareStatement(sql);
//给SQL问号赋值的过程
// 赋值 几个 什么类型
for(int i=0;i<values.length;i++){
pstat.setObject(i+1,values[i]);
}
//-------------------------------------------
//执行操作啦---查询
ResultSet rs = pstat.executeQuery();
if(rs.next()){
//将rs中的数据取出来 存入一个新的容器里 domain 数组 集合
//1. rs数据取出来 存入一个新的容器--->domain map
//2. 策略模式----多态的时候 银行取钱 去银行取钱的流程固定 每一人执行结果不一样
// 一个类负责制定流程(方法) 提供一个参数(接口) 真正传参数的时候具体子类
// public void test(接口 a){
// 1.
// 2.
// 3.不一样 a.方法();
// 4.
// 5.
// }
// Test t = new Test();
// t.test(a子类对象); 子类重写之后的效果
// 将rs的信息拿出来 组装成一个对象
obj = rm.mapperRow(rs);
//3. 利用反射来完成
}
//关闭
rs.close();//rs就可以关闭啦
pstat.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
return (T)obj;
}
//设计一个小弟方法
//负责处理所有DAO的多条查询
// 参数
// 返回值
// select * from student where ssex = ? and sage = ?
// values 男 18
// 三行记录
public <T> List<T> selectList(String sql, RowMapper rm, Object...values){
List<T> list = new ArrayList();
String className = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/newtest?useSSL=false&characterEncoding=utf8";
String username = "root";
String password = "123456";
try {
//加载驱动
Class.forName(className);
//获取连接
Connection conn = DriverManager.getConnection(url,username,password);
//创建状态参数----预处理
PreparedStatement pstat = conn.prepareStatement(sql);
//给SQL问号赋值的过程
// 赋值 几个 什么类型
for(int i=0;i<values.length;i++){
pstat.setObject(i+1,values[i]);
}
//-------------------------------------------
//执行操作啦---查询
ResultSet rs = pstat.executeQuery();
while(rs.next()){
//将rs中的数据取出来 存入一个新的容器里 domain 数组 集合
//1. rs数据取出来 存入一个新的容器--->domain map
//2. 策略模式----多态的时候 银行取钱 去银行取钱的流程固定 每一人执行结果不一样
// 一个类负责制定流程(方法) 提供一个参数(接口) 真正传参数的时候具体子类
// public void test(接口 a){
// 1.
// 2.
// 3.不一样 a.方法();
// 4.
// 5.
// }
// Test t = new Test();
// t.test(a子类对象); 子类重写之后的效果
// 将rs的信息拿出来 组装成一个对象
T obj = (T)rm.mapperRow(rs);
list.add(obj);
//3. 利用反射来完成
}
//关闭
rs.close();//rs就可以关闭啦
pstat.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
//方案二(利用反射将上述的方法参数values处理一下)
public void update(String sql,Object obj){
//Object obj可以理解为是原来的那个domain
//新增
//insert into student values(#{sid},#{sname},#{ssex},#{sage})
//values[] sid sname ssex sage 按顺序的?
//student对象 sid sname ssex sage属性---->属性值拼到对应的问号位置上?
//如果用户是按照上面的SQL写过来的
//我们需要做什么???
// 1.解析SQL---> 要每一个#{key} 将#{key}替换回原来的?
// 一条原来形式的sql语句 String sql
// 一堆key 集合ArrayList ArrayList<String> 对象
SQLAndKey sqlAndKey = handler.parseSQL(sql);
//执行JDBC的流程
String className = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/newtest?useSSL=false&characterEncoding=utf8";
String username = "root";
String password = "123456";
try {
//加载驱动
Class.forName(className);
//获取连接
Connection conn = DriverManager.getConnection(url,username,password);
//创建状态参数----预处理
PreparedStatement pstat = conn.prepareStatement(sqlAndKey.getSQL());
//给SQL问号赋值的过程
// 赋值 几个 什么类型
// 问号的值存储在一个对象里的obj 对象里面的值和SQL拼接在一起
handler.handlerParameter(pstat,obj,sqlAndKey.getKeyList());
//执行操作啦
pstat.executeUpdate();
//关闭
pstat.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//方案二(利用反射将selectOne方法处理一下 rm values)
public <T>T selectOne(String sql,Object obj,Class resultType){
Object result = null;
try {
//1.解析sql--带#{key}
SQLAndKey sqlAndKey = handler.parseSQL(sql);
//2.获取连接
String className = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/newtest?useSSL=false&characterEncoding=utf8";
String username = "root";
String password = "123456";
//加载驱动
Class.forName(className);
//获取连接
Connection conn = DriverManager.getConnection(url,username,password);
//创建状态参数----预处理
PreparedStatement pstat = conn.prepareStatement(sqlAndKey.getSQL());
//拼接SQL和问号的值
handler.handlerParameter(pstat,obj,sqlAndKey.getKeyList());
//-------------------------------------------
//执行操作啦---查询
ResultSet rs = pstat.executeQuery();
if(rs.next()){
//组装对象 将rs里面的信息取出来 组装成一个对象
result = handler.handlerResult(rs,resultType);
}
//关闭
rs.close();//rs就可以关闭啦
pstat.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
return (T)result;
}
}