回顾:
反射: 把类中成员抽取为其他类对象的过程。就是反射。
反射类: Class--->实例化类对象。
获取属性类对象: Field
获取方法类对象: Method
获取构造方法类对象: Constructor
概述:
手写ORM框架: O(Object 对象) R(Relative 关系) M(Mapping 映射) 对象关系映射。把数据中的表映射为java中的实体类,表中的一条记录映射为java实体类对象。表中的列映射实体的属性了。
之前我们学习过一个ORM---Mybatis.-----单表的CRUD无需写任何sql语句。
我们自己手写的ORM框架使用的技术: 泛型 注解 反射。
目录:
1.创建maven工程并引入相关依赖
2.创建一个连接数据库的工具类。
3.创建一个父类BaseDao
技术细节:
1.创建maven工程并引入相关依赖
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
</dependencies>
2.创建一个连接数据库的工具类。
代码演示:
jdbc.driverName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/qy165?serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=123456789
代码演示:
package com.aaa.util;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* 创建一个连接数据库的工具类
*/
public class Dbutil {
//硬编码
private static String driverName = "";
private static String url = "";
private static String username = "";
private static String password = "";
static{
//静态代码块。----随着类的加载而被加载,而且只会被加载一次。
try {
//加载属性文件
InputStream inputStream = ClassLoader.getSystemResourceAsStream("db.properties");
//属性工具类
Properties properties = new Properties();
//加载属性文件
properties.load(inputStream);
//获取驱动名称
driverName = properties.getProperty("jdbc.driverName");
url = properties.getProperty("jdbc.url");
username = properties.getProperty("jdbc.username");
password = properties.getProperty("jdbc.password");
}catch (Exception e){
System.out.println("名字为db.properties的属性文件不存在");
e.printStackTrace();
}
}
/**
* 获取链接对象
* @return
* @throws Exception
*/
public static Connection getConnection() throws Exception {
//驱动名称
Class.forName(driverName);
//获取链接对象
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
//关闭资源
public static void closeAll(Connection connection, PreparedStatement ps,ResultSet rs) {
try {
if (rs!=null) {
rs.close();
}
if (ps!=null) {
ps.close();
}
if(connection!=null) {
connection.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
/**
*测试是否能够拿到链接对象
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
System.out.println(getConnection());
}
}
3.创建一个父类BaseDao
以后子类DAO继承BaseDao类后,无需再写任何sql语句,而就可以完成单表的crud操作。
比如: StudentDao extends BaseDao DeptDao extends BaseDao.
BaseDao对所有表的CRUD操作都能通用。
代码演示:
package com.aaa.dao;
import com.aaa.annotation.TableField;
import com.aaa.annotation.TableId;
import com.aaa.annotation.TableName;
import com.aaa.util.Dbutil;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.function.DoubleBinaryOperator;
public class BaseDao<T>{
Class<T> clazz;
public BaseDao(){
//获取BaseDao子类的反射类对象
Class<? extends BaseDao> aClass = this.getClass();
//获取当前Dao子类的父类的反射类
ParameterizedType parameterizedType = (ParameterizedType) aClass.getAnnotatedSuperclass();
//得到泛型的反射类
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
clazz = (Class<T>) actualTypeArguments[0];
System.out.println(clazz);
}
//根据主键查询一条记录
//sql: select * from 表名 where 主键=值;
public T selectById(Object id) throws Exception{
StringBuffer sql=new StringBuffer("select * from ");
TableName annotation = clazz.getAnnotation(TableName.class);
String tableName=clazz.getSimpleName();
if(annotation!=null){
tableName=annotation.value();
}
sql.append(tableName+" where ");
Field[] fields = clazz.getDeclaredFields();
for(Field field:fields){
TableId tableId = field.getAnnotation(TableId.class);
if(tableId!=null){
sql.append(tableId.value()+"='"+id+"'");
continue;
}
}
//执行sql语句
Connection connection = Dbutil.getConnection();
PreparedStatement ps=connection.prepareStatement(sql.toString());
ResultSet rs = ps.executeQuery();
T t =null;
while (rs.next()){
t= clazz.newInstance();
Field[] declaredFields = clazz.getDeclaredFields();
for(Field field:declaredFields){
field.setAccessible(true);
String name = field.getName();
TableField tableField = field.getAnnotation(TableField.class);
TableId tableId = field.getAnnotation(TableId.class);
if(tableField!=null){
name=tableField.value();
}
if(tableId!=null){
name=tableId.value();
}
Object v = rs.getObject(name);
field.set(t,v);
}
}
return t;
}
//通用的删除
//通用的删除sql语句:delete from 表名 where 主键名=值;
public int delete(Object id)throws Exception {
StringBuffer sql = new StringBuffer("delete from");
TableName annotation = clazz.getAnnotation(TableName.class);
String tableName = clazz.getSimpleName();
if (annotation != null){
tableName = annotation.value();
}
sql.append(tableName +" where ");
Field[] fields = clazz.getDeclaredFields();
for (Field field:fields){
TableId tableId = field.getAnnotation(TableId.class);
if (tableId!=null){
sql.append(tableId.value()+"='"+id+"'");
continue;
}
}
//执行sql语句
//获取链接对象
Connection connection = Dbutil.getConnection();
//拿到PreparedStatement对象
PreparedStatement ps = connection.prepareStatement(sql.toString());
int i = ps.executeUpdate();
return i;
}
//通用的添加操作
//通用的添加sql语句:insert into 表名(列名,列名...) values(值,值...)
public int insert(T t)throws Exception{
//拼接的sql语句
StringBuffer sql = new StringBuffer("insert into ");
//获取表名,实体类名称就是表名。如果表名和实体类名不一致那可以起一个注解,标记一下表名,表名就是注解里的值
//根据对象获取Class反射类
Class<?> aclass = t.getClass();
//获取反射类上的注解对象
TableName annotation = aclass.getAnnotation(TableName.class);
String tableName = "";
//注解不等于空,表名等于注解值
if(annotation!= null){
tableName=annotation.value();
//如果等于空,表名等于类明,getSimpleName获取反射类类名
}else{
tableName=aclass.getSimpleName();
}
//拼接的sql语句,把表名拼接到insert into后面
sql.append(tableName);
//获取该t实体类所有的属性对象;id name loc
Field[] declaredFields = aclass.getDeclaredFields();
//用来放所有的属性名的集合
ArrayList<String> columns = new ArrayList<>();
//用来放所有的属性值的集合
ArrayList<String> values = new ArrayList<>();
//遍历属性对象
for (Field field : declaredFields){
//拿到属性名
String name = field.getName();
TableId annotation2 = field.getAnnotation(TableId.class);
if (annotation2 != null||name.equals("id")){
continue;
}
//如果注解值等于空
TableField annotation1 = field.getAnnotation(TableField.class);
//如果注解名称不等于空,那么列名就等于注解名称
if(annotation1!= null){
name = annotation1.value();
}
field.setAccessible(true);
//拿到该属性或者列对应的值
Object o = field.get(t);
//把属性名添加进去
columns.add(name);
//把属性值添加进去;加单引号的目的让任意类型都用字符串类型
values.add("'"+o+"'");
}
String columnNames = columns.toString().replace("[", "( ").replace("]", " )");
String columnValues = values.toString().replace("[", "( ").replace("]", " )");
sql.append(columnNames);
sql.append("values");
sql.append(columnValues);
//执行sql语句
//获取链接对象
Connection connection = Dbutil.getConnection();
//拿到PreparedStatement对象
PreparedStatement ps = connection.prepareStatement(sql.toString());
int i = ps.executeUpdate();
return i;
}
//修改方法
//通用的修改sql语句:update 表名 set 列名=值,列名=值...where 主键名=值;
public int update(T t)throws Exception {
StringBuilder sql = new StringBuilder("update ");
//获取实体类的反射类
Class<?> aclass = t.getClass();
//获取反射类上指定的注解对象
TableName annotation = aclass.getAnnotation(TableName.class);
String tableName = aclass.getSimpleName();
if (annotation != null) {
tableName = annotation.value();
}
sql.append(tableName + " set ");
//获取所有的Field
Field[] declaredFields = aclass.getDeclaredFields();
String where = " where ";
for (Field field : declaredFields) {
field.setAccessible(true);
String name = field.getName();
TableId tableId = field.getAnnotation(TableId.class);
if (name.equals("id") ) {
where = where + " id='" + field.get(t)+ "'";
continue;
}
if (tableId!=null) {
where = where + tableId.value()+"'"+field.get(t)+ "'";
continue;
}
TableField tableField = field.getAnnotation(TableField.class);
if (tableField != null) {
name = tableField.value();
}
String value = "'" + field.get(t) + "'";
sql.append(name + " = " + value + ",");
}
String sql2 = sql.toString().substring(0, sql.length()-1)+where;
//执行sql语句
//获取链接对象
Connection connection = Dbutil.getConnection();
//拿到PreparedStatement对象
PreparedStatement ps = connection.prepareStatement(sql2);
int i = ps.executeUpdate();
return i;
}
}
总结:
在其他项目中创建lib包,引用即可