1.学习视频:JDBC封装(3)—>直播
2.学习内容
1)多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。(比如一个人有不同的身份)
2)简单了解lambda表达式,也可以叫匿名函数,没有函数名字,用lambda声明。允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
3)复习反射**:
类是用来描述一组对象
反射机制认为是用来描述一组类
反射就是在运行状态中:
- 获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等- 获取任意对象的属性,并且能改变对象的属性- 调用任意对象的方法- 判断任意一个对象所属的类- 实例化任意一个类的对象
1>Class中的常用方法
1.int result = getModifiers();// 获取类的修饰符(权限 特征)
2.String name = getName();// 获取名字 类全名—>包名+类名
3.String simpleName = getSimpleName(); //简单名 类名
4.Package p = getPackage(); // 获取包名
p.getName();
5.Class sclazz = getSuperClass();// 获取超类(父类)
6.Class[] classes = getInterface();// 获取当前clazz的所有父亲接口
(1~6为类中的一些特征)
7.Object obj = newInstance();// 默认调用无参数构造方法创建对象
8.Field f = getField(“属性名”); 通过属性名获取,会抛出一个异常,NoSuchFieldException找不到该属性 可能是属性名拼写出错
Field[] fs = getFields();// 可以获取全部的名字包括继承关系
如上的两个方法只能获取公有的属性 但是包含继承过来的父类属性
9.getDeclaredField(“属性”);
Field[] fs = getDeclaredFields();
如上的两个方法能获取公有的和私有的属性 但是只能获取本类中的属性
2>Field类中的常用方法
int = getModifers()
Class = getType()
String = getName()
set(对象,值) 操作属性 向里面存值
值= get(对象) 操作属性 从里面取值
setAccessable(true); // 可以修改属性被操作
4)int.class是什么?
int.class是Class对象的引用,严格来说是原始类型int的Class对象引用。
举一个例子:假设你自己建立了一个Dog类,那么Dog.class就是Dog类型的Class对象引用。
每一个类加载之后,类加载器会给建立Class对象,你可以用Class.forName()方法取得该对象的引用
比如,Class
同时,你也可以用Class
其实Dog.class本身就是引用int.class是int类型的Class对象引用。
int类型是原始类型,JVM运行时,int类型Class对象已经建立,那么如何获取该对象的引用呢?
就是用int.class。 另外一种方法是用Integer.TYPE静态成员变量来获取。
两者区别:
int.class是一个引用,注意是一个引用,而不是类
Integer是int类型的包装类,注意是一个类,而不是引用
3.练习的代码
package orm;import java.util.ArrayList;/*** 这是一个我们自己规定的对象* 对象的目的是为了解析SQL后用来存储最终结果的* 应该有两个属性* ArrayList 按顺序存放解析出来的全部key* String 用来存放还原回去的那个原来的sql*/public class SQLAndKey {private ArrayList<String> keyList = new ArrayList<>();private StringBuilder newsql = new StringBuilder();public SQLAndKey(ArrayList<String> keyList, StringBuilder newsql) {this.keyList = keyList;this.newsql = newsql;}public ArrayList<String> getKeyList() {return keyList;}public String getNewsql() {//便于读取return newsql.toString();}}
package orm;import domain.Bank;import java.sql.*;import java.util.ArrayList;import java.util.HashSet;import java.util.List;/*** 这是一个单独提出来的类* 就是原来的小弟方法* 升级为大哥 小弟方法是通用的 可以为任何一个DAO提供支持*/public class SqlSession {private String className = "com.mysql.jdbc.Driver";private String url = "jdbc:mysql://localhost:3306/bank?characterEncoding=UTF-8&useSSL=false";private String user = "root";private String password = "root";//1.参数中的这个Objcet... 原来是个对象--[] 可读性不好//2.用户在使用的时候 传递参数还需要自己来指定顺序 用起来不是很方便// 将那个恶心的参数设计回s对象形式 Object... Atm// 如何去处理原来SQL上面的问号 与 Atm对象中的属性 一一对应的问题//设计一个方法 处理任何一个表格的任何一个增删改操作// 参数 SQL SQL上面的那些值 atmpublic void all(String sql,Object obj){try {//0.SQL解析// insert into atm values(#{aname},#{apassword},#{abalance})// 解析这里面的每一个#{key}// 将所有的key拿到 找寻obj对象中对应的属性值// 带有#{}这样的SQL 还原回带?的SQL 交给pstat对象 让他拼接SQLAndKey sqlAndKey = handler.parseSQL(sql);//1.加载驱动Class.forName(className);//2.获取连接Connection conn = DriverManager.getConnection(url,user,password);//3.创建状态参数(sql)PreparedStatement pstat = conn.prepareStatement(sqlAndKey.getNewsql());//4.将SQL和那些值拼接在一起if(obj!=null){handler.handleParameter(pstat,obj,sqlAndKey.getKeyList());}//5.执行executeUpdate操作pstat.executeUpdate();//6.关闭资源pstat.close();conn.close();} catch (Exception e) {e.printStackTrace();}}}
**
package orm;import java.lang.reflect.Field;import java.sql.PreparedStatement;import java.sql.SQLException;import java.util.ArrayList;import java.util.Map;/***SqlSession对象的小弟 (二弟)* 负责帮助SqlSession做一些琐事*/public class Handler {//insert into atm values(#{aname},#{apassword},#{abalance})//设计一个方法 负责解析带有#{key}形式的SQL//参数 SQL语句//返回值// 1.得到所有的key都按照顺序存起来 好多key --> 容器 --> 集合ArrayList<String>// 2.原来带有 ? 的SQL-->String类型// 对象{ArrayList,String}public SQLAndKey parseSQL(String sql){//public 可省略//1.提供两个新的变量 用来分别存储解析后的两部分ArrayList<String> keyList = new ArrayList<>();//存储列名StringBuilder newsql = new StringBuilder();//存储除去标识符换成 ? 的正常sql语句//2.解析//insert into atm values(#{aname},#{apassword},#{abalance} order by xxx)while (true) {//可能不止一个key//找寻#{ }的位置int left = sql.indexOf("#{");int right = sql.indexOf("}");//严谨的判断if (left != -1 && right != -1 && left < right) {//找到了#{} 截取中间的key-->列名newsql.append(sql.substring(0, left));//保留前半部分newsql.append("?");keyList.add(sql.substring(left + 2, right));} else {//sql中已经没有成组的#{}newsql.append(sql);//拼接剩下的sqlbreak;}sql = sql.substring(right + 1);}//将两个变量组合成一个对象 返回return new SQLAndKey(keyList,newsql);}//=======================================================================================//设计一个方法 负责将keyList中的所有key遍历 找到对象中的值 交给pstat对象拼接完整//参数 pstat(干活),obj(值),keyList(所有的key和顺序)//没有返回值public void handleParameter(PreparedStatement pstat,Object obj,ArrayList<String> keyList) throws SQLException {//获取obj对应的那个Class类Class clazz = obj.getClass();// clazz通常应该是什么类型的// 就是基础类型 int--Integer float--Float double--Double String// 就是domain类型 atm student// map--->容器 仅次于domain类型if(clazz==int.class || clazz==Integer.class){//int.class就是利用反射找int的属性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 || 2.domainif(obj instanceof Map) {this.setMap(pstat,obj,keyList);}else {this.setObject(pstat,obj,keyList);}}}//设计一个方法 负责帮助handleParameter方法拼接 -->map类型private void setMap(PreparedStatement pstat,Object obj,ArrayList<String> keyList) throws SQLException {Map map = (Map) obj;for(int i=0;i<keyList.size();i++){pstat.setObject(i+1,map.get(keyList));}}//设计另一个方法 负责帮助handleParameter方法拼接对象--> domainprivate void setObject(PreparedStatement pstat,Object obj,ArrayList<String> keyList) {try {Class clazz = obj.getClass();for(int i=0;i<keyList.size();i++){//每次循环找到一个keyString key = keyList.get(i);//通过key去obj对象中 获取属性-->反射Field field = clazz.getDeclaredField(key);//直接操作私有属性-->修改属性被操作field.setAccessible(true);//获取属性值Object value = field.get(obj);//交给pstat存入即可pstat.setObject(i+1,value);}} catch (IllegalAccessException e) {e.printStackTrace();} catch (SQLException throwables) {throwables.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();}}}
//dao-->(DataAccessobjects 数据存取对象)是指位于业务逻辑和持久化数据之间实现对持久化数据的访问。通俗来讲,就是将数据库操作都封装起来。
public class BankDao {
// 方法openSession()返回一个SqlSession对象
private SqlSession sqlSession = new SqlSession();
//数据库新增
public void insert(Bank bank){
//String sql = "insert into bs values(?,?,?)";
String sql = "insert into bs values(#{bname},#{bpassword},#{bmoney})";
//sqlSession.insert(sql,bank);
sqlSession.all(sql,bank);
}
//数据库删除
public void delect(String bname){
//String sql = "delete from bs where bname = ?";
String sql = "delete from bs where bname = #{bname}";
sqlSession.delete(sql,bname);
}
public static void main(String[] args) {
Bank bank = new Bank("小小","111",600f);
BankDao dao = new BankDao();
dao.insert(bank);
}
封装了三个方法(重载)
增删改查—->参数采用了Object对象方式(基础类型 domain map)
public void insert(Map map){
1.提供一个sql insert into atm values (#{aname},#{apassword},#{})
2.提供的是给sql使用的值map
3.调用sqlSession.insert(sql,map);
}
总结:
很多设计有很多方式去解决,可能集合、数字、对象等都可以实现,但是除了实现功能以外还需要考虑最优选择,业务层是为了实现功能,底层设计封装的时候才会更多体现思想层面的设计,不仅仅是功能的体现。在实现基础功能的基础上还需要多思考有没有可以优化或者更好的解决办法,以及为什么选择这个。
SQL语句的增、删、改、查方法有很多种,在传参数时,传SQL语句可以用数组,但是由于数组长度固定,但不能确定用户SQL语句中的变量 ? 的个数,所有可以选择动态数组。 为了增进代码的通用性,可以让任何一个表格可以使用,设计一种方法,将参数设计回对象模式,对SQL语句进行拆分和拼接,在之前参数中,用户在传参时就定义了列名顺序,换成对象传参后,则需要将SQL语句的 ? 加标识符来区分,创建一个方法去拆分,保留标识符并更换 ? 拼接成SQL语句,并保留标识符—>#{key},在将这条完整的SQL传给dao执行,而保留的标识符就是列名顺序,设计一个方法去读取遍历,将列名再返回给执行方法与 ? 一 一对应。
存在的问题:代码可以看懂,设计的思路也懂,但是真正去落实编码,还是有所欠缺……
