1. 引入相关依赖
<dependencies>
<!--引入lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
<!--引入单元测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--引入mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!--引入德鲁伊得依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!--单元测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<!-- <scope>test</scope>-->
</dependency>
</dependencies>
2. 准备工具类Util
① 使用JDBC获取properties文件
注意:
在使用jdbc编程时,编写的属性文件中最好以jdbc.xxx的方式命名
如:jdbc.username:root
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* @program: ORM
* @description: 没有使用连接池的数据库工具类
* @author:
* @create: 2024-07-01 19:56
**/
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 (IOException e) {
e.printStackTrace();
}
}
//获取数据库连接
public static Connection getConnection(){
Connection connection = null;
try {
Class.forName(driverName);
connection = DriverManager.getConnection(url,username,password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
//关闭所有连接
public static void closeAll(Connection connection, PreparedStatement ps, ResultSet rs){
try {
if(connection != null){
connection.close();
}
if (ps != null){
ps.close();
}
if (rs != null){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
② 使用数据源获取properties文件
注意:
在使用数据源的情况下,例如本次使用的德鲁伊数据源,在编写属性文件时,就不需要再加 jdbc. 了
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
/**
* @program: ORM
* @description: 使用了连接池的数据库工具类
* @author:
* @create: 2024-07-02 09:19
**/
public class DbUtilDataSource {
protected static Connection connection;
protected static PreparedStatement ps;
protected static ResultSet rs;
protected static DataSource dataSource;
static {
try {
Properties properties = new Properties();
InputStream resourceAsStream =
DbUtilDataSource.class.getClassLoader().getResourceAsStream("db.properties");
properties.load(resourceAsStream);
dataSource= DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
try {
connection = dataSource.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
return connection;
}
public static void closeAll(Connection connection, PreparedStatement ps, ResultSet rs) {
try {
if (connection != null) {
connection.close();
}
if (ps != null) {
ps.close();
}
if (rs != null) {
rs.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3. 自定义注解
① 定义表名和实体类名的关系
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 定义表名和实体类名的关系
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableName {
String value();
}
② 定义列名和实体类属性名的关系
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 定义列名和实体类属性名的关系
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnName {
String value();
}
③ 定义id自增的列
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 定义id自增的列
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnId {
String value();
}
4. 编写baseDao
使用泛型的方式“T”获取子类对象
public class BaseDao<T> {}
① insert方法
//添加功能
public int add(T t) {
//拼接insert的sql语句--insert into 表名(字段名) values(值)
/**
* 拼接第一步:insert into
*/
StringBuilder sql = new StringBuilder("insert into ");
//获取表名--表名和实体类名一致;表名和实体类名不一致(使用注解获取)
//根据对象获取反射类
Class<?> aClass = t.getClass();
//获取注解对象--如果表名和实体类名不一致,则使用注解获取
TableName table = aClass.getAnnotation(TableName.class);
/**
* 拼接第二步:表名
*/
//定义表名
String tableName = "";
if (table != null) {
tableName = table.value();
} else {
tableName = aClass.getSimpleName();
}
sql.append(tableName);
/**
* 拼接第三步:字段名 values 值
*/
//获取该类中所有的属性名
Field[] fields = aClass.getDeclaredFields();
//定义一个集合来存储所有列名
List<String> columnNames = new ArrayList<String>();
//定义一个集合存储values值
List<Object> values = new ArrayList<Object>();
//逐个获取所有属性名,并添加到集合当中
for (Field field : fields) {
//设置私有属性可访问规则
field.setAccessible(true);
//考虑列名和属性名不一致的情况,不一致时使用注解标注
ColumnName column = field.getAnnotation(ColumnName.class);
//考虑列名为自增时,不用获取情况
ColumnId columnId = field.getAnnotation(ColumnId.class);
//定义列名
String columnName = "";
if (columnId != null) {
//如果该属性是自增列,则跳过
continue;
} else if (column != null) {
//如果不一致,把注解标注的列名添加到sql语句中
columnName = column.value();
} else {
//如果一致,则直接获取类的属性名
columnName = field.getName();
}
try {
//把列名存储到集合中
columnNames.add(columnName);
//获取属性值
Object value = field.get(t);
//把属性值存储到集合中
values.add("'" + value + "'");
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
//返回该集合的字符串形式,并将【】换成()
String colNames = columnNames.toString().replace("[", "(").replace("]", ")");
String vals = values.toString().replace("[", "(").replace("]", ")");
sql.append(colNames);
sql.append(" values");
sql.append(vals);
System.out.println(sql.toString());
/**
* 最后一步!执行sql语句
*/int row = 0;
try {
//获取连接
Connection connection = DButil.getConnection();
//预执行sql语句
PreparedStatement ps = connection.prepareStatement(sql.toString());
//执行sql语句获取返回值
row = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return row;
}
② update方法
//修改功能 update 表名 set 字段名=值 where id=?
public int update(T t) {
StringBuilder updateSql = new StringBuilder("update ");
String where = " where ";
//获取反射类
Class<?> aClass = t.getClass();
//获取表名
String tableName = "";
TableName table = aClass.getAnnotation(TableName.class);
if (table != null) {
tableName = table.value();
} else {
tableName = aClass.getSimpleName();
}
updateSql.append(tableName + " set ");
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
//设置私有属性可访问规则
field.setAccessible(true);
Object value = null;
try {
//获取属性值
value = field.get(t);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
//获取列名和自增id
String columnName = "";
ColumnId columnId = field.getAnnotation(ColumnId.class);
ColumnName column = field.getAnnotation(ColumnName.class);
if (columnId != null) {
//如果是主键id,拼接where条件
String columnIdName = columnId.value();
where += (columnIdName + "=" + "'" + value + "'");
continue;
} else if (column != null) {
columnName = column.value();
} else {
columnName = field.getName();
}
updateSql.append(columnName + "=" + "'" + value + "',");
}
//删除最后一个逗号
updateSql.deleteCharAt(updateSql.length() - 1);
//System.out.println("不带where的update:" + updateSql.toString());
//添加where
updateSql.append(where);
//System.out.println("where:"+where);
//System.out.println("带where的update:" + updateSql.toString());
/**
* 最后一步!执行sql语句
*/int row = 0;
try {
//获取连接
Connection connection = DButil.getConnection();
//预执行sql语句
PreparedStatement ps = connection.prepareStatement(updateSql.toString());
//执行sql语句获取返回值
row = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return row;
}
③ delete方法
难点解析
– 删除功能根据实际场景常常需要传入一个id值或者关键信息来进行删除,而不是传入整个整个实体对象。
– 所以在这里就不能再采取传入泛型T t的方式,选用传入一个object类型的参数
– 但是问题就来了,之前传入的是泛型T t,创建studentDao,则t对应便是studentDao,然后根据t来获取反射类,再获取列名,字段名等信息。而现如今入的是一个object对象,所以无法获取反射类
解决:
定义一个构造方法,在basedao加载时,该构造方法便会执行
在该构造方法中
this指代的是继承baseDao的子类,通过".getClass()"获取到子类(user啊,student啊等)的反射类
再通过".getGenericSuperclass()"获取到父类(BaseDao)
最后通过".getActualTypeArguments()"的方式,获取到父类的参数列表,即我们想要的泛型
//定义当前对象的反射类
private Class<T> clazz;
//定义构造方法 随着类的加载而加载--获取子类dao继承basedao的泛型
public BaseDao(){
//获取当前子类的反射类 this表示子类Dao对象
Class<? extends BaseDao> aClass = this.getClass();
//调用getGenericSuperclass()方法获取当前子类的父类的反射类
//父类的返回类型为type,要用到其子类的方法,所以强转为ParameterizedType
ParameterizedType genericSuperclass = (ParameterizedType) aClass.getGenericSuperclass();
//获取该反射类中的泛型类型
Type actualTypeArgument = genericSuperclass.getActualTypeArguments()[0];
clazz= (Class) actualTypeArgument;
}
delete方法
//删除功能 delete from 表名 where 主键列名=id
public int deleteByid(Object id){
//1. 定义delete from
StringBuffer deleteSql=new StringBuffer("delete from ");
//2. 获取表名
String tableName=clazz.getSimpleName();
/**
* 考虑表名和实体类不一致的情况,给不一致的实体类上加注解,定义数据库表的真实表名
*/
TableName annotation = clazz.getAnnotation(TableName.class);
if(annotation!=null){
tableName = annotation.value();
}
//3. 拼接where条件
deleteSql.append(tableName+" where ");
//4. 获取where条件列名(常常为主键id)
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field:declaredFields){
/**
* 考虑主键id是自增情况,所以常常加了id注解。通过注解的方式直接获取到id值,然后拼接,break结束循环。
*/
ColumnId tableId = field.getAnnotation(ColumnId.class);
if(tableId!=null){
deleteSql.append(tableId.value()+"="+"'"+id+"'");
break;
}
}
System.out.println("deleteSql:"+deleteSql);
/**
* 最后一步!执行sql语句
*/
int row = 0;
try {
//获取连接
Connection connection = DButil.getConnection();
//预执行sql语句
PreparedStatement ps = connection.prepareStatement(deleteSql.toString());
//执行sql语句获取返回值
row = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return row;
}
④ 根据id查询
//根据id查询
//sql语句:select * from 表名 where id=值
public T getById(Object id) throws Exception {
//sql语句
StringBuilder sql = new StringBuilder("select * from ");
//获取表名
String tableName = clazz.getSimpleName();
//考虑实体类名与表名不一致的情况
//注解获取表名
TableName annotation = clazz.getAnnotation(TableName.class);
if (annotation != null) {
tableName = annotation.value();
}
sql.append(tableName + " where ");
//获取属性名称
Field[] declaredFields = clazz.getDeclaredFields();
//循环遍历
for (Field f : declaredFields) {
//获取主键
ColumnId tableId = f.getAnnotation(ColumnId.class);
if (tableId != null) {
//获取属性名称
String value = tableId.value();
//追加到sql语句中
sql.append(value + "='" + id + "'");
//跳出循环
break;
}
}
//执行sql语句
Connection con = DButil.getConnection();
//获取执行sql语句的对象
PreparedStatement ps = con.prepareStatement(sql.toString());
//执行sql语句,获取查询的结果集
ResultSet rs = ps.executeQuery();
//通过泛型实例化一个当前子类对象,接下来循环遍历,为对象赋值
T t = clazz.newInstance();
while (rs.next()) {
//遍历循环属性名称
for (Field f : declaredFields) {
// 暴力访问私有属性
f.setAccessible(true);
//处理主键
ColumnId an_ID = f.getAnnotation(ColumnId.class);
if (an_ID != null) {
//如果获取到了主键,则将主键值赋值给对象
f.set(t, rs.getObject(an_ID.value()));
} else {
//处理其他属性
ColumnName an_columnName = f.getAnnotation(ColumnName.class);
if (an_columnName != null) {
f.set(t, rs.getObject(an_columnName.value()));
} else {
f.set(t, rs.getObject(f.getName()));
}
}
}
}
//System.out.println(sql);
return t;
}
⑤ 查询所有
//查询所有
public List<T> getAll() throws Exception {
//定义集合,存储查询结果
List<T> list=new ArrayList<T>();
//sql语句
StringBuilder sql = new StringBuilder("select * from ");
//获取表名
String tableName = clazz.getSimpleName();
//考虑实体类名与表名不一致的情况
//注解获取表名
TableName an_tableName = clazz.getAnnotation(TableName.class);
if (an_tableName != null) {
tableName = an_tableName.value();
}
sql.append(tableName);
//执行sql语句
Connection con = DButil.getConnection();
PreparedStatement ps = con.prepareStatement(sql.toString());
ResultSet rs = ps.executeQuery();
while (rs.next()){
T t=clazz.newInstance();
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field:declaredFields){
field.setAccessible(true);
ColumnId an_columnId = field.getAnnotation(ColumnId.class);
if(an_columnId!=null){
field.set(t,rs.getObject(an_columnId.value()));
}else{
ColumnName an_columnName = field.getAnnotation(ColumnName.class);
if(an_columnName!=null){
field.set(t,rs.getObject(an_columnName.value()));
}else{
field.set(t,rs.getObject(field.getName()));
}
}
}
list.add(t);
}
return list;
}