1. 程式人生 > >深入理解Java的Annotation系列-第五部分 使用註解編寫ORM框架

深入理解Java的Annotation系列-第五部分 使用註解編寫ORM框架

一、什麼是ORM?

物件關係對映(英語:(Object Relational Mapping,簡稱ORM,或O/RM,或O/Rmapping),是一種程式技術,隨著面向物件的發展而產生的。用來把物件模型表示的物件對映到基於S Q L 的關係模型資料庫結構中去,或者把表中的一條記錄自動封裝成物件。

         

採用ORM優勢在於,程式設計師採用面向物件的方法來訪問資料庫,簡化了編碼,提高了開發速度,ORM自動完成類和表,屬性名和欄位名的對映。

    使用本ORM框架,在處理查詢業務邏輯時,只要編寫形如"from 類名"的SQL語句,就可以得到封裝好的所查詢所有結果的List集合,處理新增業務時,只要呼叫Add(obj)方法即可實現新增功能。其他的功能讀者可自行新增。

二、整體思路

1、自定義ORM註解,包含@Table和@Column。

   2、寫一個類,解析註解資訊,並存入Map物件

   3、實現查詢和新增功能

   4、使用自定義的ORM框架,實現業務邏輯

   5、測試

三、具體實現

1、整體架構

     

   2、建表並插入資料的SQL

       showdatabases;

       create database czf;

       use czf;

       create table stu(id int primary key,namevarchar(40),age int);

       insert into stuvalues(1,'czf',34),(2,'afeng',25);

       select * from stu;

       createtable teacher(tid int primary key,tname varchar(40),tage int);

       insertinto teacher values(1,'czf',34),(2,'afeng',25);

       insert into teacher(tid,tname,tage)values(3,'czf3',33);

        select * from teacher;

3、具體程式碼

                 第一部分  ORM部分程式碼的編寫

   3.1 Table.java

     packagecn.com.bochy.annotation;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

/**

 * 自定義註解,用於註解類名所對應的表名

 * @authorchenzhengfeng

 */

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

public @interface Table {

      String value();//表名

}

3.2 Column.java

   package cn.com.bochy.annotation;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

/**

 * 自定義註解,用於註解物件的屬性名所對應的表的列名

 * @authorchenzhengfeng

 */

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.FIELD)

public @interface Column {

   String value();//列名

}

3.3 OrmImpl.java

   package cn.com.bochy.orm;

import java.lang.reflect.Field;

import java.util.HashMap;

import java.util.Map;

import cn.com.bochy.annotation.Column;

import cn.com.bochy.annotation.Table;

/**

 * 解析註解資訊,並存入Map

 * @authorchenzhengfeng

 */

public class OrmImpl {

    //用於儲存類名對應的表名,其中key是類名,value是表名

    private Map<String,String>mapTable=newHashMap<String,String>();

    //用於屬性名對應的表的欄位名,其中key是屬性名,value是欄位名

    private Map<String,String>mapColumns=newHashMap<String,String>();

    public Map<String, String> getMapTable() {

       returnmapTable;

    }

    public void setMapTable(Map<String, String> mapTable) {

       this.mapTable =mapTable;

    }

    private  StringtableName="";

    public String getTableName() {

       returntableName;

    }

    public void setTableName(String tableName) {

       this.tableName =tableName;

    }

    public Map<String, String> getMapColumns() {

       returnmapColumns;

    }

    public void setMapColumns(Map<String, String> mapColumns) {

       this.mapColumns =mapColumns;

    }

    /**

     * 解析實體類的註解資訊,其中c1是實體類的類物件

     * @param c1

     */

public void parseAnnotation(Class<?> c1) {

    Table table=c1.getAnnotation(Table.class);

    tableName=table.value();//獲取註解的表名

    mapTable.put(c1.getName(),tableName);//存入

    Field[] fields=c1.getDeclaredFields();//獲取所有的屬性名

    for(Fieldf:fields) {//遍歷屬性名

       if(!f.getName().equals("serialVersionUID")) {

           Column c=f.getAnnotation(Column.class);

           mapColumns.put(f.getName(),c.value());//獲取該屬性名所對應的欄位,並存入

       }

    }

}

}

3.4  OrmQuery.java

package cn.com.bochy.orm;

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.ArrayList;

import java.util.List;

import java.util.Map;

import cn.com.bochy.dbutil.DBHepler;

/**

 * 實現查詢功能

 * @authorchenzhengfeng

 *

 */

public class OrmQuery {

    DBHepler helper=new DBHepler();

    private static OrmImpl orm=null;

    private static String tableName="";

    private static Map<String,String> mapColumns=null;//儲存屬性和欄位對應關係的MAP

    private static Map<String,String> mapTables=null;//儲存類名和表名對應關係的MAP

    private Class<?>classc1;//查詢物件的類物件

    /**

     * 通過構造方法實現初始化工作

     * @param c1

     */

    public OrmQuery(Class<?>c1) {

       this.classc1=c1;

        orm=new OrmImpl();

           orm.parseAnnotation(classc1);//解析要查詢物件的註解資訊

           tableName=orm.getTableName();

           mapColumns=orm.getMapColumns();

           mapTables=orm.getMapTable();

    }

    /**

     * 查詢sql形如"from類名"格式的語句,並封裝成List,其他的形式請讀者自行新增

     * @param hql

     * @return

     */

    @SuppressWarnings("rawtypes")

    public List query(Stringhql){ 

        int index=hql.indexOf("from");

        /**

         * 以下語句是從hql語句中擷取類名

         */

        StringclassName="";//類名

        StringfullclassName="";//包含包名的類名

        Stringsql="";

        Stringhqlsub=hql.substring(index+5);//form的長度為4,再加上空格,所以為5

        className=hqlsub;

           //遍歷mapTables,查詢類名對應的表名

        for(Stringkey:mapTables.keySet()) {

           if(key.contains(className)) {

               tableName=mapTables.get(key);

               fullclassName=key;

           }

        }

        sql="select * from "+tableName;

     ResultSet rs= helper.query(sql, null);

    List<Object>lists=new ArrayList<Object>();

        try {

           while(rs.next()) {         

              Class<?>c1=  Class.forName(fullclassName);  //查詢物件的類物件

              Objects=    c1.newInstance();//建立一個查詢物件

           Field[]fs=   c1.getDeclaredFields();//獲取該物件的所有屬性

             for(Fieldf:fs) {//遍歷屬性

               String fname= f.getName();//獲取某個屬性名

              StringsetMethod="set"+FirstBig(fname);//某個屬性名對應的setter方法名

                   Method[] methods=c1.getDeclaredMethods();//獲取該物件的所有方法

                   for(Methodm:methods) {//遍歷方法

                       if(m.getName().equals(setMethod)) {//如果找到對應的方法

                          String columnName=mapColumns.get(fname);//屬性名對應的欄位名

                             m.invoke(s,rs.getObject(columnName));//執行該setter方法,給物件m賦值

                          break;

                       }

                   }

             }

              lists.add(s);//把物件存入列表

           }

       } catch (SQLException |ClassNotFoundException | InstantiationException | IllegalAccessExceptione) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       } catch(IllegalArgumentExceptione) {

           // TODO Auto-generatedcatch block

           e.printStackTrace();

       } catch (InvocationTargetExceptione) {

           // TODO Auto-generatedcatch block

           e.printStackTrace();

       }

      return lists;  

    }

    //把第一個字元變為大寫

    public String FirstBig(Stringstr) {

        returnstr.substring(0,1).toUpperCase()+str.substring(1);

    }

}

3.5  OrmAdd.java

package cn.com.bochy.orm;

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

import java.util.ArrayList;

import java.util.List;

import java.util.Map;

import cn.com.bochy.dbutil.DBHepler;

/**

 * 實現ORM中的新增功能

 * @authorchenzhengfeng

*/

public class OrmAdd {

    DBHepler helper=new DBHepler();

    private static OrmImpl orm=null;

    private static Map<String,String> mapColumns=null;

    private Class<?>classc1;//指定要新增的物件的類型別

    //通過構造方法初始化

    public OrmAdd(Class<?>c1) {

       this.classc1=c1;

        orm=new OrmImpl();

           orm.parseAnnotation(classc1);//解析註解資訊

           mapColumns=orm.getMapColumns();//獲取屬性名和欄位名的對應資訊

    }

    /**

     * 通過Java的反射機制結合註解資訊實現新增功能

     * @param obj

     * @return

     */

   public boolean add(Object obj) {

       //獲取表名
     for(String key:mapTables.keySet()) {
    if(key.equals(obj.getClass().getName())) {
    tableName=mapTables.get(key);
    }
     }

       Method[] methods= obj.getClass().getDeclaredMethods();//獲取新增物件中所有的方法

       Field[] fileds=obj.getClass().getDeclaredFields();//獲取新增物件中所有的屬性

       String sqlColumn="";//用於拼接要新增表中所有的欄位

       String sqlvalue="";//用於拼接要新增的所有值,用?代替

      List<Object> parmas=newArrayList<Object>();//儲存要新增的所有值

       for(Fieldf:fileds)//遍歷屬性

       for(Methodm:methods) {//遍歷方法

               //獲取該屬性對應的getter方法

           if(m.getName().equals("get"+FirstBig(f.getName()))) {      

               Object values1;

              try {

                  values1 = m.invoke(obj);//呼叫該物件的getter方法

                  parmas.add(values1);//把新增的值存入列表

                  sqlColumn+=mapColumns.get(f.getName())+",";

                  sqlvalue+="?,";

              } catch (IllegalAccessExceptione) {

                  // TODO Auto-generatedcatch block

                  e.printStackTrace();

              } catch (IllegalArgumentExceptione) {

                  // TODO Auto-generatedcatch block

                  e.printStackTrace();

              } catch (InvocationTargetExceptione) {

                  // TODO Auto-generatedcatch block

                  e.printStackTrace();

              }          

           }

       }

       sqlColumn=CutoutLastChar(sqlColumn);//把最後一個","去掉

       sqlvalue=CutoutLastChar(sqlvalue);//把最後一個","去掉

       String sql="insert into "+tableName+"("+sqlColumn+")"+" values("+sqlvalue+")";

      Object[] param=parmas.toArray();//把集合列表轉化為陣列

    return helper.noQuery(sql, param); //呼叫helper物件中noQuery實現新增功能

   }

   /**

    * 去掉字串最後一個字元

    * @param str

    * @return

    */

   public String CutoutLastChar(Stringstr) {

       returnstr.substring(0,str.length()-1);

   }

   /**

    * 把字串的首字元變為大寫

    * @param str

    * @return

    */

    public String FirstBig(Stringstr) {

        returnstr.substring(0,1).toUpperCase()+str.substring(1);

    }

}

3.6 DBHepler.java

package cn.com.bochy.dbutil;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

/**

 * 簡單的JDBC的封裝

 * @authorchenzhengfeng

 *

 */

public class DBHepler {

    private  Stringurl="jdbc:mysql://localhost:3306/czf";

    private  Stringuser="root";

    private  Stringpassword="root";

    public  Connectionconn=null;

    public  PreparedStatementpstmt=null;

    public  ResultSetrs=null;

   //1、得到連線物件

   public  ConnectiongetConnection(){

       try {

       Class.forName("com.mysql.jdbc.Driver");

       conn=DriverManager.getConnection(url,user, password);

    } catch (ClassNotFoundExceptione) {

       // TODO Auto-generatedcatch block

       e.printStackTrace();

    } catch (SQLExceptione) {

       // TODO Auto-generatedcatch block

       e.printStackTrace();

    }

    return conn

   }

   //2、關閉資源

   public  void closeAll(){

       try {

       if(rs!=null){

              rs.close();

          }

          if(pstmt!=null){

              pstmt.close();

          }

          if(conn!=null){

              conn.close();

          }

    } catch (SQLExceptione) {

       // TODO Auto-generatedcatch block

       e.printStackTrace();

    }

   }

    //3、通用查詢方法

   public  ResultSetquery(Stringsql,Object[] param){

       try {

          getConnection();

       pstmt=conn.prepareStatement(sql);

       if(param!=null)

       for(inti=0;i<param.length;i++){

           pstmt.setObject(i+1,param[i]);

       }

       rs=pstmt.executeQuery();

    } catch (SQLExceptione) {

       // TODO Auto-generatedcatch block

       e.printStackTrace();

    }

       returnrs;

   }

   //4、通用非查詢方法

   public  boolean noQuery(String sql,Object[]param){

       getConnection();

       int a=0;

       try {

       pstmt=conn.prepareStatement(sql);

       if(param!=null)

           for(inti=0;i<param.length;i++){

              pstmt.setObject(i+1,param[i]);

           }

       a=pstmt.executeUpdate();

    } catch (SQLExceptione) {

       // TODO Auto-generatedcatch block

       e.printStackTrace();

    }

       closeAll();

       if(a>0)

       return true;

       else

          return false;

   }

}

             第二部分  ORM應用

3.7  實體類Stu.java

package cn.com.bochy.entity;

import java.io.Serializable;

import cn.com.bochy.annotation.Column;

import cn.com.bochy.annotation.Table;

/**

 * 實體類

 * @author Administrator

 *

 */

@Table("stu")//通過註解,類Stu對應表stu

public class Stu implements Serializable {

    private static final long serialVersionUID = 1L;

    @Column("id")//通過註解,屬性名id對應欄位名(列名)id

    private int id;

public int getId() {

       returnid;

    }

    public void setId(intid) {

       this.id =id;

    }

public String getName() {

       returnname;

    }

    public void setName(String name) {

       this.name =name;

    }

    public int getAge() {

       returnage;

    }

    public void setAge(intage) {

       this.age =age;

    }

    @Column("name")//通過註解,屬性名name對應欄位名(列名)name

private String name;

    @Column("age")//通過註解,屬性名age對應欄位名(列名)age

  private int age;

  public Stu() {

    // TODO Auto-generatedconstructor stub

}

}

3.8 實體類Teacher.java

package cn.com.bochy.entity;

import java.io.Serializable;

import cn.com.bochy.annotation.Column;

import cn.com.bochy.annotation.Table;

@Table("teacher")//通過註解,Teacher對應表teacher

public class Teacher implements Serializable {

    private static final long serialVersionUID = 1L;

    @Column(value="tid")//通過註解,屬性名id對應欄位名(列名)tid

    private int id;

public int getId() {

       returnid;

    }

    public void setId(intid) {

       this.id =id;

    }

public String getName() {

       returnname;

    }

    public void setName(String name) {

       this.name =name;

    }

    public int getAge() {

       returnage;

    }

    public void setAge(intage) {

       this.age =age;

    }

    @Column("tname")//通過註解,屬性名name對應欄位名(

相關推薦

深入理解ajax系列

前面的話   一般地,使用readystatechange事件探測HTTP請求的完成。XHR2規範草案定義了進度事件Progress Events規範,XMLHttpRequest物件在請求的不同階段觸發不同型別的事件,所以它不再需要檢査readyState屬性。這個草案

深入理解Java的Annotation系列-部分 使用註解編寫ORM框架

一、什麼是ORM? 物件關係對映(英語:(Object Relational Mapping,簡稱ORM,或O/RM,或O/Rmapping),是一種程式技術,隨著面向物件的發展而產生的。用來把物件模型表示的物件對映到基於S Q L 的關係模型資料庫結構中去,或者把表中的

深入理解Java的Annotation系列-部分 註解的應用-使用註解實現許可權管理

一、整體思路 1、先自定義一個用於許可權管理的註解 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Authority {      String modu

深入理解Tomcat系列:Context容器和Wrapper容器

ssa stream servlet實例 可用 igel sse ould rip alt 前言 Context容器是一個Web項目的代表,主要管理Servlet實例,在Tomcat中Servlet實例是以Wrapper出現的。如今問題是怎樣才幹通過C

深入理解ajax系列四篇

blob selected 單選框 encode 展示 條目 種類型 %20 寫代碼 前面的話   現代Web應用中頻繁使用的一項功能就是表單數據的序列化,XMLHttpRequest 2級為此定義了FormData類型。FormData為序列化表單以及創建與表單格式相同的

深入理解ajax系列八篇

row 用戶數據 方便 案例 默認方法 span target osi content 前面的話   在以前,網站的用戶與後端交互的主要方式是通過HTML表單的使用。表單的引入在1993年,由於其簡單性和易用性,直到電子商務出現之前一直保持著重要位置。理解表單提交,對於更深

深入理解ajax系列七篇

gin 開發工程師 tar component fin hasattr mar tex 員工 前面的話   雖然ajax全稱是asynchronous javascript and XML。但目前使用ajax技術時,傳遞JSON已經成為事實上的標準。因為相較於XML而言,J

深入理解計算機系統----章優化程式效能

轉載地址https://www.jianshu.com/p/4586dc676807 編寫執行的快的程式有三個因素:①選擇合適的演算法和資料結構;②理解編譯器的能力,使用有效的方式讓編譯器能進行優化;③對於運算量特別大的程式,可能還需要進行任務分解。在這一過程中可能還需要對程式的可讀性和執

深入理解AJAX系列四篇--跨域問題

我們先了解一下域名地址的組成:http:// www . google : 8080 / script/jquery.js   http:// (協議號)   www (子域名)   google (主域名)    8080 (埠號)   scrip

深入理解ajax系列三篇——響應解碼

前面的話   我們接收到的響應主體型別可以是多種形式的,包括字串String、ArrayBuffer物件、二進位制Blob物件、JSON物件、javascirpt檔案及表示XML文件的Document物件等。下面將針對不同的主體型別,進行相應的響應解碼 屬性   在介紹響

一個絕對注意不到的小細節(深入理解計算機系統章5.5及5.6)

   下面的計算計算多項式的兩種不同方法,形如 a0+a1x+a2x^2········     第二個函式是根據horner法,通過反覆提出冪,來減少乘法的次數,按照道理說,既然polyh函式比poly函式減少了乘法的次數,那應該比poly快才對,可是事實正好相反,poly

深入理解javascript作用域系列

彈出 例子 深入理解java logs title 最終 pre 有變 context 前面的話   對於執行環境(execution context)和作用域(scope)並不容易區分,甚至很多人認為它們就是一回事,只是高程和犀牛書關於作用域的兩種不同翻譯而已。但實際上,

深入理解閉包系列篇——閉包的10種形式

前面的話   根據閉包的定義,我們知道,無論通過何種手段,只要將內部函式傳遞到所在的詞法作用域以外,它都會持有對原始作用域的引用,無論在何處執行這個函式都會使用閉包。接下來,本文將詳細介紹閉包的10種形式 返回值   最常用的一種形式是函式作為返回值被返回 var F = function()

深入理解javascript作用域系列篇——一張圖理解執行環境和作用域

前面的話   對於執行環境(execution context)和作用域(scope)並不容易區分,甚至很多人認為它們就是一回事,只是高程和犀牛書關於作用域的兩種不同翻譯而已。但實際上,它們並不相同,卻相互糾纏在一起。本文先用一張圖開宗明義,然後進行術語的簡單解釋,最後根據圖示內容進行詳細說明 圖示

部分 架構篇 十三章 MongoDB Replica Sets 架構(成員深入理解

儘管客戶端不能直接通過Secondary進行寫操作,客戶端能通過secondary進行資料讀取操作,一個secondary也可以在primary不可達的情況下轉換成primary,同時也可以自主制定一些配置來賦予secondary特殊功能:

深入理解計算機系統 三章大略和章大略

$0 一個 編譯 存儲器 系統 32位 做了 ++i 擴展 這2章總結的很少,主要是覺得沒那麽重要。 1.2個操作數的指令,第二個操作數通常是目的操作數:movb a b,move a to b,而add a b,b+=a,指令分為指令類,如mov類:movb,movw,m

深入理解計算機系統_第一部分_三章_程式的機器級表示

深入,並且廣泛 -沉默犀牛 文章導讀 計算機執行機器程式碼,用位元組序列編碼低階的操作,包括處理資料、管理記憶體、讀寫儲存裝置上的資料,以及利用網路通訊。編譯器基於程式語言的規則、目標機器的指令集和作業系統遵循的慣例,經過一系列的階段生成機器程式碼。GCC C語言編譯器以彙

[章] 深入理解計算機系統三版 家庭作業參考答案

5.13 A. 畫圖: 關鍵路徑為第三幅圖加粗部分 B. 下界為浮點加法的延遲界限,CPE 為 3.00 C. 整數加法的延遲界限,CPE 為 1.00 D. 關鍵路徑上只有浮點加法 5.14 v

前端學PHP之面向對象系列篇——對象操作

fas get toolbar 影響 運算 描述 ssa reference 保持 對象克隆   對象復制,又叫對象克隆,可以通過 clone 關鍵字來完成   在多數情況下,我們並不需要完全復制一個對象來獲得其中屬性。但有一個情況下確實需要:如果你有一個窗口對象,該對象持

深入理解JavaScript系列

復用 類型 橋接 職責鏈模式 學習 組合 設計模式之代理模式 職責 理論 該文用於自我學習~ 來自:湯姆大叔 深入理解JavaScript系列文章,包括了原創,翻譯,轉載,整理等各類型文章,如果對你有用,請推薦支持一把,給大叔寫作的動力。 深入理解JavaScript系