手撕ORM框架
1.建立Maven工程
2.匯入依賴
<dependencies>
<!-- 引入 jdbc 的依賴 -->
<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>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
</dependencies>
3.設計運算元據庫的工具類
public class DbUtil {
// 德魯伊連線池
private static DataSource dataSource;
// 使用靜態程式碼塊載入配置檔案
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();
}
}
public static Connection getConnection(){
try {
Connection connection = dataSource.getConnection();
return connection;
} catch (SQLException e) {
e.printStackTrace();
} finally {
}
return null;
}
public static void closeAll(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet){
if(resultSet != null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(preparedStatement != null){
try {
preparedStatement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
4.建立配置檔案 db.properties
#資料來源資訊
url=jdbc:mysql://localhost:3306/demo?serverTimezone=Asia/Shanghai
username=root
password=123456
driverClass=com.mysql.cj.jdbc.Driver
5.設計自定義註解
// 修飾表名
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableName {
String value();
}
// 修飾主鍵Id
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableId {
String value() default "id";
}
// 修飾表欄位
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableField {
String value();
}
6.實體類中使用自定義註解
@TableName("表名")
@Data
public class Student {
@TableId("主鍵名")
private Integer id;
@TableField("列名")
private String name;
@TableField("列名")
private Integer age;
}
7.設計BaseDao
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
clazz = (Class) actualTypeArgument;
}
public int insert(T t) {
try{
// 設計通用的新增
// 1.首先寫出新增的 sql insert into 表名(欄位名) values(屬性名)
// 這裡注意 into 後面的空格
StringBuffer sql = new StringBuffer("insert into ");
// 2.獲取實體類所對應的表名
// 2.1自定義註解 @TableName 標識實體類所對應的表
// 2.2拿到實體類的反射類,利用反射獲取註解,進而獲得表的名稱
Class clazz = t.getClass();
// 實體類的名稱
String simpleName = clazz.getSimpleName();
// 表的名稱
String tableName = "";
// 獲取類上的註解
TableName annotation = (TableName) clazz.getAnnotation(TableName.class);
if(annotation != null){
tableName = annotation.value();
}
// 追加拿到的表名
sql.append(tableName);
// 3.獲取所有的列名和對應的屬性名
// 3.1用來儲存資料庫中對應的列
List<String> columnNames = new ArrayList<String>();
// 3.2用來儲存需要插入的值
List<Object> values = new ArrayList<Object>();
// 4.獲取所有的屬性物件
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 4.1獲取類中的屬性名
String name = field.getName();
// 4.2獲取屬性上的註解
// 獲取主鍵的註解
TableId tableId = field.getAnnotation(TableId.class);
// 獲取成員的註解
TableField tableField = field.getAnnotation(TableField.class);
// 4.3根據拿到的值進行過濾
if(tableId != null){
// 4.3.1 此時拿到的是主鍵的註解 退出本輪迴圈
continue;
}
if(tableField != null){
// 4.3.2 此時拿到的是成員的註解
// 覆蓋掉類中的屬性名 使用表中的欄位名
name = tableField.value();
}
// 對私有屬開啟暴力反射
field.setAccessible(true);
// 獲取物件屬性對應的值
Object value = field.get(t);
// 將值經過新增引號後加入到值list中
values.add("'"+value+"'");
// 將屬性列加入到列list中
columnNames.add(name);
}
// 5.將列list轉換成字串,然後使用字串替換方法將其轉換成需要的格式
String columnList = columnNames.toString().replace("[","(").replace("]", ")");
// 6.將值list轉換成字串,然後使用字串替換方法將其轉換成需要的格式
String valueList = values.toString().replace("[","(").replace("]",")");
// 7.合併字串
sql.append(columnList + " values " + valueList);
System.out.println(sql);
// 8.執行插入語句
Connection connection = DbUtil.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());
int i = preparedStatement.executeUpdate();
return i;
} catch (Exception e){
e.printStackTrace();
}
return 0;
}
public int update(T t){
try{
//設計通用的修改
// 1.設計 sql update 表名 set 屬性名=值,...
StringBuffer sql = new StringBuffer("update ");
// 2.獲取表名
// 2.1獲取物件的反射類例項
Class clazz = t.getClass();
// 2.2拿到物件對應的表名 類的簡名
String tableName = clazz.getSimpleName();
TableName tableNameAnnotation = (TableName) clazz.getAnnotation(TableName.class);
if (tableNameAnnotation != null){
// 2.3拿到表的名稱 使用註解的名稱進行覆蓋
tableName = tableNameAnnotation.value();
}
// 3.將拿到的表名追加到sql後面
sql.append(tableName + " set ");
// 4.設定條件部分
String where = "where ";
// 5.獲取所有的列物件
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 5.1獲取所有的屬性名
String name = field.getName();
// 5.2獲取屬性上的註解
// 5.2.1獲取主鍵的註解
TableId tableId =(TableId) field.getAnnotation(TableId.class);
// 5.2.2獲取屬性的註解
TableField tableField =(TableField) field.getAnnotation(TableField.class);
// 6.設定私有屬性允許訪問
field.setAccessible(true);
// 7.獲取屬性的值
Object value = field.get(t);
// 根據註解的種類進行選擇性拼接
if(tableId != null){
String id = tableId.value();
where += id + "='" + value + "'";
// 拼接完成退出本輪迴圈
continue;
}
if(tableField != null){
name = tableField.value();
}
sql.append(name + "='" + value + "',");
}
// 去除 sql 中最後一個 ,
sql.deleteCharAt(sql.length() - 1).append(where);
System.out.println(sql);
// 執行 sql 語句
Connection connection = DbUtil.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());
int i = preparedStatement.executeUpdate();
return i;
} catch (Exception e){
e.printStackTrace();
}
return 0;
}
public int deleteById(Object id){
try {
// 設計刪除的 sql 語句
StringBuffer sql = new StringBuffer("delete from ");
// 獲取實體類的名稱
String tableName = clazz.getSimpleName();
// 獲取表名的註解
TableName tableNameAnnotation = clazz.getAnnotation(TableName.class);
// 判斷是否新增了@TableName註解
if(tableNameAnnotation != null){
tableName = tableNameAnnotation.value();
}
// 追加 where條件
sql.append(tableName + " where ");
// 獲取所有的屬性物件
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
TableId tableId =(TableId) field.getAnnotation(TableId.class);
if(tableId != null){
sql.append(tableId.value() + "=" + id);
// 跳出迴圈
break;
}
}
// 執行 sql
Connection connection = DbUtil.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());
int i = preparedStatement.executeUpdate();
return i;
} catch (Exception e){
e.printStackTrace();
}
return 0;
}
}
8.使用者定義Dao使用BaseDao
public class StudentDao extends BaseDao <Student>{}
public class TeacherDao extends BaseDao <Teacher>{}
9.測試
public class Test {
@org.junit.Test
public void test01(){
// 測試新增
StudentDao studentDao = new StudentDao();
Student student = new Student();
student.setId(1);
student.setName("李白");
student.setAge(18);
studentDao.insert(student);
}
@org.junit.Test
public void test02(){
// 測試更新
StudentDao studentDao = new StudentDao();
Student student = new Student();
student.setId(1);
student.setName("李白");
student.setAge(25);
studentDao.update(student);
}
@org.junit.Test
public void test03(){
// 測試刪除
StudentDao studentDao = new StudentDao();
Student student = new Student();
student.setId(1);
studentDao.deleteById(student.getId());
}
}