1.技术栈: java+mysql+反射+自定义注解+泛型+jdbc.
2.持久层框架: 与数据库交互的一层称为持久层(dao)
3.o:(Object对象) r:(relative关系) m:(mapping映射)。 实体类---数据库表 属性--表的字段 实体类对象--一条记录 集合---表中多条记录。
4.手撕持久层框架: 自己编写持久层框架 可以完成无需写sql语句即可完成对单表的CRUD操作。
1.创建一个maven的java工程
2.引入相关依赖以及配置数据源的属性文件
引入依赖:
<dependencies>
<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.16</version>
</dependency>
</dependencies>
配置数据源信息:
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/your
username=your
password=your
3.创建DbUtil工具类
DBUtil:获取连接对象和关闭连接资源
public class DBUtil {
/**
* 数据源,用于管理数据库连接。
*/
private static DataSource dataSource;
/**
* 静态代码块,在类加载时初始化数据源。
* 通过读取db.properties文件配置数据库连接参数,并使用Druid数据源。
*/
static {
try {
Properties properties = new Properties();
InputStream inputStream = DBUtil.class.getClassLoader().getResourceAsStream("db.properties");
properties.load(inputStream);
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接。
*
* @return 数据库连接对象。
* @throws Exception 如果获取连接失败抛出异常。
*/
public static Connection getConnection() throws Exception {
return dataSource.getConnection();
}
/**
* 关闭数据库连接及相关资源。
*
* @param conn 数据库连接对象。
* @param ps 预编译的SQL语句对象。
* @param rs 查询结果集对象。
*/
public static void closeAll(Connection conn, PreparedStatement ps, ResultSet rs) {
try {
if (rs != null) rs.close();
if (ps != null) ps.close();
if (conn != null) conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
4.自定义注解 用于实体类
TableName:表明和实体类名不一致时使用
TableField:列名和属性名不一致时使用
TableId:列名和属性名不一致时使用
/**
* 定义一个字段级别的注解,用于标记实体类中的字段对应数据库表中的列名。
*
* @Target(ElementType.FIELD) 表示该注解只能用于字段上。
* @Retention(RetentionPolicy.RUNTIME) 表示该注解在运行时仍然有效,可被反射访问。
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableField {
/**
* 返回数据库表中对应的列名。
*
* @return 列名字符串。
*/
String value();
}
/**
* 定义一个字段级别的注解,用于标记实体类中的主键字段。
* 默认情况下,如果未指定主键名,则认为主键名为"id"。
*
* @Target(ElementType.FIELD) 表示该注解只能用于字段上。
* @Retention(RetentionPolicy.RUNTIME) 表示该注解在运行时仍然有效,可被反射访问。
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableId {
/**
* 返回数据库表中的主键列名,默认值为"id"。
*
* @return 主键列名字符串。
*/
String value() default "id";
}
/**
* 定义一个类型级别的注解,用于标记实体类对应数据库表的名称。
*
* @Target(ElementType.TYPE) 表示该注解只能用于类上。
* @Retention(RetentionPolicy.RUNTIME) 表示该注解在运行时仍然有效,可被反射访问。
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableName {
/**
* 返回数据库表的名称。
*
* @return 表名字符串。
*/
String value();
}
5.BaseDao类:父类
如何获取类名中泛型T:
// this表示子类Dao对象 Class<? extends BaseDao> aClass = this.getClass(); // 获取当前子类的父类的反射类 ParameterizedType genericSuperclass = (ParameterizedType) aClass.getGenericSuperclass(); // 获取该反射类型中的泛型类型 Type actualTypeArgument = genericSuperclass.getActualTypeArguments()[0]; clazz = (Class) actualTypeArgument;
接下来是BaseDao中的CRUD操作:
public class BaseDao<T> {
private Class<T> clazz;
public BaseDao() {
// this表示子类Dao对象
Class<? extends BaseDao> aClass = this.getClass();
// 获取当前子类的父类的反射类
ParameterizedType genericSuperclass = (ParameterizedType) aClass.getGenericSuperclass();
// 获取该反射类型中的泛型类型
Type actualTypeArgument = genericSuperclass.getActualTypeArguments()[0];
clazz = (Class) actualTypeArgument;
}
// 通用删除方法
public int delete(Object id) throws Exception {
//1.创建一个sql字符串
StringBuffer sql = new StringBuffer("delete 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 field : declaredFields) {
TableId tableId = field.getAnnotation(TableId.class);
if (tableId != null) {
sql.append(tableId.value() + "=" + id);
break;
}
}
Connection connection = DBUtil.getConnection();
PreparedStatement ps = connection.prepareStatement(sql.toString());
int i = ps.executeUpdate();
return i;
}
// 通用添加方法
public int insert(T t) throws Exception {
//1.创建一个sql字符串
StringBuffer sql = new StringBuffer("insert into ");
//2. 获取实体类的反射类.
Class<?> aClass = t.getClass();
//3.获取表名
String tableName = aClass.getSimpleName();//实体类的名称
TableName annotation = aClass.getAnnotation(TableName.class);
if (annotation != null) {
tableName = annotation.value();
}
sql.append(tableName);
//4.获取所有的列名--对于属性名
List<String> columnNames = new ArrayList<>();
List<Object> values = new ArrayList<>();
//5. 获取所有的属性对象
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field : declaredFields) {
//获取属性名
String name = field.getName();
//获取属性上的注解
TableId tableIdAnnotation = field.getAnnotation(TableId.class);
TableField fieldAnnotation = field.getAnnotation(TableField.class);
if (tableIdAnnotation != null) {
continue;
}
if (fieldAnnotation != null) {
name = fieldAnnotation.value();
}
field.setAccessible(true);
//对象属性的值
Object value = field.get(t);
values.add("'" + value + "'");
columnNames.add(name);
}
String replace = columnNames.toString().replace("[", "(").replace("]", ")");
String replace1 = values.toString().replace("[", "(").replace("]", ")");
sql.append(replace + " values " + replace1);
//执行sql语句
Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql.toString());
int i = ps.executeUpdate();
return i;
}
// 通用修改方法
public int update(T t) throws Exception {
StringBuffer sql = new StringBuffer("update ");
// 获取表名
Class<?> aClass = t.getClass();
String tableName = aClass.getSimpleName();
TableName annotation = aClass.getAnnotation(TableName.class);
if (annotation != null) {
tableName = annotation.value();
}
sql.append(tableName).append(" set ");
String where = " where ";
// 获取所有列对象
Field[] declaredFields = aClass.getDeclaredFields();
boolean hasId = false;
for (Field field : declaredFields) {
// 属性名
String name = field.getName();
TableField fieldAnnotation = field.getAnnotation(TableField.class);
TableId tableId = field.getAnnotation(TableId.class);
field.setAccessible(true);
Object value = field.get(t);
// 如果为主键注解
if (tableId != null) {
String tableIdName = tableId.value();
where += tableIdName + "='" + value + "'";
hasId = true;
continue;
}
if (fieldAnnotation != null) {
name = fieldAnnotation.value();
}
sql.append(name).append("='").append(value).append("',");
}
sql.deleteCharAt(sql.length() - 1).append(where);
// 执行 SQL 语句
Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql.toString());
int i = ps.executeUpdate();
return i;
}
// 根据ID查询
public T findById(Object id) throws Exception {
StringBuffer sql = new StringBuffer("select * from ");
String tableName = clazz.getSimpleName();
TableName annotation = clazz.getAnnotation(TableName.class);
if (annotation != null) {
tableName = annotation.value();
}
sql.append(tableName).append(" where ");
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
TableId tableId = field.getAnnotation(TableId.class);
if (tableId != null) {
sql.append(tableId.value()).append("=").append(id);
break;
}
}
Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql.toString());
ResultSet rs = ps.executeQuery();
if (rs.next()) {
T t = clazz.getDeclaredConstructor().newInstance();
for (Field field : declaredFields) {
field.setAccessible(true);
Object value = rs.getObject(field.getName());
field.set(t, value);
}
return t;
}
return null;
}
// 查询全部
public List<T> findAll() throws Exception {
List<T> resultList = new ArrayList<>();
StringBuffer sql = new StringBuffer("select * from ");
String tableName = clazz.getSimpleName();
TableName annotation = clazz.getAnnotation(TableName.class);
if (annotation != null) {
tableName = annotation.value();
}
sql.append(tableName);
Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql.toString());
ResultSet rs = ps.executeQuery();
Field[] declaredFields = clazz.getDeclaredFields();
while (rs.next()) {
T t = clazz.getDeclaredConstructor().newInstance();
for (Field field : declaredFields) {
field.setAccessible(true);
Object value = rs.getObject(field.getName());
field.set(t, value);
}
resultList.add(t);
}
return resultList;
}
}
6.entity实体类
这里因为使用Lombok所以就没有getter和setter方法了,它可以通过注解自动生成 getter、setter、构造函数、equals 方法、hashCode 方法等常用方法。
@TableName(value = "tb_dept")
@Data
public class Dept {
@TableId(value = "id")
private Integer id;
@TableField(value = "name")
private String name;
}
@TableName(value = "tb_emp")
@Data
public class Emp {
private Integer id;
private String name;
private Integer age;
private String job;
private Double salary;
private Date entrydate;
private Integer managerid;
private Integer deptid;
private String head;
}
7.测试
这里就偷了个懒,都写在一起了,接下来应该还有把项目打成jar,下期再写吧!
public class Test {
public static void main(String[] args) throws Exception {
DeptDao deptDao = new DeptDao();
Dept dept = new Dept();
List<Dept> result = deptDao.findAll();
System.out.println("查询结果: " + result);
Dept result2 = deptDao.findById(1);
System.out.println("查询结果: " + result2);
dept.setId(12);
dept.setName("测试");
int result3 = deptDao.insert(dept);
System.out.println("插入结果: " + result3);
dept.setId(12);
dept.setName("测试2");
int result4 = deptDao.update(dept);
System.out.println("更新结果: " + result4);
int result5 = deptDao.delete(12);
System.out.println("删除结果: " + result5);
}
}
欢迎大家评论区指出不足,一起学习进步!