1. 程式人生 > >使用註解和反射構建通用資料庫框架

使用註解和反射構建通用資料庫框架

下面我們寫一個操作資料庫框架的示例,從而熟悉註解,反射,泛型的使用。

    通過實體類建立和刪除操作,因為操作資料庫中的表不同,那麼對應的實體類也不同,我們需要使用泛型來指定操作的實體類。使用註解使實體類和資料庫表列一一對應。

DbHelper.java

public class DbHelper extends SQLiteOpenHelper {
	private static String NAME = "db.db";
	private static int VERSION = 1;

	public DbHelper(Context context) {
		super(context, NAME, null, VERSION);
	}

	@Override
	public void onCreate(SQLiteDatabase db) {

	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

	}

}


    Dao.java

public interface Dao<T> {
	/**
	 * 插入資料
	 * 
	 * @param t
	 */
	public long insert(T t);

	/**
	 * 通過id刪除資料
	 * 
	 * @param id
	 */
	public int delete(Serializable id);

	/**
	 * 通過id更新資料
	 * 
	 * @param t
	 */
	public int update(T t);

	/**
	 * 查詢資料中所有資料
	 * 
	 * @return
	 */
	public List<T> finadAll();

	/**
	 * 建立表
	 */
	public void createTable();
}

在其實現類DaoImp.java

其部分程式碼

public abstract class DaoImp<T> implements Dao<T> {

	private SQLiteDatabase db;

	public DaoImp(DbHelper helper) {
		super();
		db = helper.getWritableDatabase();
	}

	@Override
	public long insert(T t) {
		if (!isTableExists()) {
			return -1;
		}
		ContentValues values = new ContentValues();
		fillContentValues(t, values);// 第一個引數標示資料的來源;第二個引數設定給誰
		// 此處省略n行程式碼
		return db.insert(getTableName(), null, values);
	}

	@Override
	public int delete(Serializable id) {
		if (!isTableExists()) {
			return -1;
		}
		return db.delete(getTableName(), getIdName(null) + "=?",
				new String[] { id.toString() });
	}

	@Override
	public int update(T t) {
		if (!isTableExists()) {
			return -1;
		}
		ContentValues values = new ContentValues();
		fillContentValues(t, values);

		return db.update(getTableName(), values, getIdName(t) + "=?",
				new String[] { getId(t) });
	}
在建立表的時候,如果此表已經存在,那麼就不需要建立,否則就建立此表
public boolean isTableExists() {
		String sql = "select count(*) as c from sqlite_master where type ='table' and name ='"
				+ getTableName() + "';";
		Cursor cursor = db.rawQuery(sql, null);
		if (cursor.moveToNext()) {
			int count = cursor.getInt(0);
			if (count > 0) {
				return true;
			}
		}
		return false;
	}

這是查詢資料庫中所有的表,看指定的表是否存在。

通過bean建立表,使用的註解使實體類和表中列一一對應起來。

指定表名的註解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableName {
	String value();
}
指定表主鍵id註解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableId {
	String value();

	boolean isAutoIncrease();
}

指定列註解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
	String value();
}

下面以一個Book.java實體類為示例,看看這些自定義註解的使用。
@TableName("book")
public class Book {
	@TableId(value = "serialnum", isAutoIncrease = true)
	@Column("serialnum")
	private int serialNum;
	@Column("name")
	private String name;
	@Column("des")
	private String des;

	public Book() {
		super();
	}

	public Book(String name, String des) {
		super();
		this.name = name;
		this.des = des;
	}

	public Book(int serialNum, String name, String des) {
		super();
		this.serialNum = serialNum;
		this.name = name;
		this.des = des;
	}

	public int getSerialNum() {
		return serialNum;
	}

	public void setSerialNum(int serialNum) {
		this.serialNum = serialNum;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDes() {
		return des;
	}

	public void setDes(String des) {
		this.des = des;
	}

	@Override
	public String toString() {
		return "Book [serialNum=" + serialNum + ", name=" + name + ", des="
				+ des + "]";
	}

}
在DaoImpl.java中建立表
@Override
	public void createTable() {

		String tableName = getTableName();
		if (TextUtils.isEmpty(tableName)) {
			return;
		}
		if (isTableExists()) {
			return;
		}
		Map<String, String> columns = columnNames();
		StringBuffer sb = new StringBuffer();
		sb.append("CREATE TABLE ");
		sb.append(tableName);
		sb.append("(");

		boolean flag = true;
		if (columns != null) {
			Set<String> keySet = columns.keySet();
			Iterator<String> iterator = keySet.iterator();
			while (iterator.hasNext()) {
				String name = iterator.next();
				String type = columns.get(name);
				if (flag) {
					sb.append(name).append("  ").append(type);
					flag = false;
				} else {
					sb.append(",").append(name).append("  ").append(type);
				}
			}
		}
		sb.append(")");
		db.execSQL(sb.toString());
	}

獲取列明和列型別map。
public Map<String, String> columnNames() {
		Map<String, String> columns = null;
		T t = getInstance();
		Field[] fields = t.getClass().getDeclaredFields();
		columns = new HashMap<String, String>();
		for (Field field : fields) {

			field.setAccessible(true);
			Column columAn = field.getAnnotation(Column.class);
			if (columAn != null) {
				TableId idAn = field.getAnnotation(TableId.class);
				if (idAn != null) {
					if (idAn.isAutoIncrease()) {
						columns.put(idAn.value(),
								"integer primary key autoincrement");

					} else {
						columns.put(idAn.value(), "varchar(50)");
					}
				} else {
					columns.put(columAn.value(), "varchar(50)");
				}

			}
		}
		return columns;
	}

通過獲取實體類中註解的型別,通過field.getAnnotation(Column.class);獲取欄位上的註解,如果是Column是列,獲取其值,就是列名稱。如果是TableId表示表的主鍵,其值就是是否自定增長。

獲取表名

/**
	 * 獲取表名字
	 * 
	 * @return
	 */
	public String getTableName() {
		T t = getInstance();
		TableName tableName = t.getClass().getAnnotation(TableName.class);
		if (tableName != null) {
			return tableName.value();
		}
		return null;
	}

獲取TableName註解。來獲取表名。

建立實體類物件。

/**
	 * 建立實體的物件
	 * 
	 * @return
	 */
	public T getInstance() {
		Class clazz = getClass();// 正在執行
		Type genericSuperclass = clazz.getGenericSuperclass();// 獲取支援泛型的父類
		if (genericSuperclass instanceof ParameterizedType) {
			Type[] actualTypeArguments = ((ParameterizedType) genericSuperclass)
					.getActualTypeArguments();
			Class target = (Class) actualTypeArguments[0];
			try {
				return (T) target.newInstance();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}

		}


在插入資料時候,通過傳入bean物件,然後向對應的列中插入資料。

private void fillContentValues(T m, ContentValues values) {
		Field[] fields = m.getClass().getDeclaredFields();
		for (Field item : fields) {
			item.setAccessible(true);// 施暴

			Column column = item.getAnnotation(Column.class);
			if (column != null) {
				TableId id = item.getAnnotation(TableId.class);
				if (id != null) {
					// 主鍵+自增
					if (id.isAutoIncrease()) {
						// 不需要設定資料到ContentValues
					} else {
						String key = column.value();
						try {
							String value = item.get(m).toString();// m.field
							values.put(key, value);
						} catch (IllegalArgumentException e) {
							e.printStackTrace();
						} catch (IllegalAccessException e) {
							e.printStackTrace();
						}
					}
				} else {
					String key = column.value();
					try {
						String value = item.get(m).toString();// m.field
						values.put(key, value);
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
				}

			}
		}
	}

獲取bean的欄位陣列,先進行暴力反射,反正欄位是私有欄位,先判斷欄位上是否有Column註解,如果有,表示此欄位是和列對應,其註解對應的值就是表示的列名,然後通過field.get(m)獲取物件其欄位對應的值,然後條件到ContentValues物件中,values.put(key, value);key是註解Column對應的值,value是通過反射獲取物件的值。

在這裡還判斷是主鍵註解TableId,如果是自增,那麼不需要新增到values中,否則就向正常的列一樣,也要新增到values中。

private void fillFields(Cursor cursor, T m) {

		Field[] fields = m.getClass().getDeclaredFields();
		for (Field item : fields) {
			item.setAccessible(true);

			Column column = item.getAnnotation(Column.class);
			if (column != null) {
				String columnName = column.value();
				int columnIndex = cursor.getColumnIndex(columnName);
				String value = cursor.getString(columnIndex);

				// 主鍵+自增:int long

				if (item.getType() == int.class) {
					try {
						item.set(m, Integer.parseInt(value));
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
				} else if (item.getType() == long.class) {
					try {
						item.set(m, Long.parseLong(value));
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
				} else {
					try {
						item.set(m, value);
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
				}

			}
		}

	}

將遊標中的查詢出來的值,封裝到bean中,即實體類中。

獲取bean中的Column註解,然後獲取其值(類名),通過columnvalue=cursor.getColumnIndex(columnName)獲取此列在遊標中的值,然後通過field.set(bean,columnvalue),設定給傳進來的bean物件欄位值。

private String getId(T t) {
		if (t == null) {
			t = getInstance();
		}
		Field[] fields = t.getClass().getDeclaredFields();
		for (Field item : fields) {
			item.setAccessible(true);
			TableId id = item.getAnnotation(TableId.class);
			if (id != null) {
				try {
					return item.get(t).toString();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

獲取主鍵的對應的列名,如果前面看通了,那麼這個方法自然也就明白。其實就是再次判斷bean類欄位上的註解。如果是TableId,那麼就獲取其值,就是表中列對應的列名。

下面介紹一個使用步驟:

BookImpl.java

public class BookImpl extends DaoImp<Book> {

	public BookImpl(DbHelper helper) {
		super(helper);

	}

}


activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/create_table"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="建立表" />

    <Button
        android:id="@+id/find_all"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="查詢" />

    <Button
        android:id="@+id/insert"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="插入" />

    <Button
        android:id="@+id/update"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="修改" />

    <Button
        android:id="@+id/delete"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="刪除" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </ScrollView>

</LinearLayout>

MainActivity.java
public class MainActivity extends Activity implements OnClickListener {

	private DaoImp<Book> bookDao;
	private TextView tv;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initView();
		DbHelper helper = new DbHelper(MainActivity.this);
		bookDao = new BookImpl(helper);
	}

	private void initView() {
		Button create_table = (Button) findViewById(R.id.create_table);
		create_table.setOnClickListener(this);
		Button delete = (Button) findViewById(R.id.delete);
		delete.setOnClickListener(this);
		Button update = (Button) findViewById(R.id.update);
		update.setOnClickListener(this);
		Button insert = (Button) findViewById(R.id.insert);
		insert.setOnClickListener(this);
		Button find_all = (Button) findViewById(R.id.find_all);
		tv = (TextView) findViewById(R.id.tv);
		find_all.setOnClickListener(this);
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.create_table:
			createTable();
			break;
		case R.id.find_all:
			findAll();
			break;
		case R.id.insert:
			insert();
			break;
		case R.id.delete:
			delete();
			break;
		case R.id.update:
			update();
			break;

		default:
			break;
		}

	}

	private void update() {
		bookDao.update(new Book(1, "updateName", "updateDes"));

	}

	private void delete() {
		bookDao.delete(2);

	}

	private void insert() {
		Book b1 = new Book("第一本", "第一本描述");
		Book b2 = new Book("第二本", "第二本描述");
		Book b3 = new Book("第三本", "第三本描述");
		bookDao.insert(b1);
		bookDao.insert(b2);
		bookDao.insert(b3);

	}

	private void findAll() {
		List<Book> finadAll = bookDao.finadAll();
		StringBuffer sb = new StringBuffer();
		tv.setText("");
		if (finadAll != null) {
			for (Book book : finadAll) {
				sb.append(book.toString()).append(" \n ");
			}
			tv.setText(sb.toString());
		}
	}

	private void createTable() {

		bookDao.createTable();
	}
}

DaoImp<Book> bookDao = new BookImpl(helper);獲取book表操作類

bookDao.createTable();建立表

bookDao.insert(b3);插入一條記錄

bookDao.delete(2);刪除id為2的記錄

bookDao.update(new Book(1, "updateName", "updateDes"));更新id為1的記錄。

效果圖:


相關推薦

使用註解反射構建通用資料庫框架

下面我們寫一個操作資料庫框架的示例,從而熟悉註解,反射,泛型的使用。     通過實體類建立和刪除操作,因為操作資料庫中的表不同,那麼對應的實體類也不同,我們需要使用泛型來指定操作的實體類。使用註解使實體類和資料庫表列一一對應。 DbHelper.java public

java註解反射的結合使用

分享 spa tac over ace 註解 [] sys string 首先反射註解,那麽保留策略必須是Runtime,[email protected]/* */(RetentionPolicy.RUNTIME) ①定義一個註解類 @Retention(R

使用自定義註解反射 ,自動生成查詢語句

runt entity forname == rop ava stat pri string 1.自定義表名註解 package com.lf.annotation; import java.lang.annotation.ElementType; import jav

JAVA 註解反射

his get jvm ava tom instance SM 通過 調用 通過反射來獲取類 Class MyTest{ private String name; public String showName{ System.out.prin

註解反射操作註解

什麼是註解? • Annotation是從JDK5.0開始引入的新技術。 • Annotation的作用: – 不是程式本身,可以對程式作出解釋。(這一點,跟註釋沒什麼區別) – 可以被其他程式(比如:編譯器等)讀取。(註解資訊處理流程,是註解和註釋的重大區別 。如果沒有註解資訊處理流程,則註解毫無

註解反射

一、註解        英文:Annotation       1.1 註解的概念          &

理解Web框架如何構建一個CSS框架

  “大量重置”瀏覽器預設風格,比如,設定所有元素的margin和padding為0,去掉框架(framesets)和圖片(images)的border,等等。  以基線對齊。這包括諸如設定塊級元素的margins相同的(或多個)基準行高,如段落(paragraph)、頭(header)、以及列表(list)

通過泛型反射構建一個簡單的集合操作工具類

      平時在操作集合物件的時候(比如List);我想一次新增大於一個數據的時候,往往需要通過一個集合物件呼叫兩次add方法,比如: List<Person> personList=new ArrayList<>(); Person p1=n

110_註解反射

[toc][https://www.bilibili.com/video/BV1p4411P7V3](https://www.bilibili.com/video/BV1p4411P7V3) # 註解 java.lang.annotation ## 什麼是註解 1. Annotation是從JDK5.0

net Mono 構建的HTTP服務框架

ots .config app pac spn 請求方法 park 找不到 get   Nancy是一個基於.net 和Mono 構建的HTTP服務框架,是一個非常輕量級的web框架。    設計用於處理 DELETE, GET, HEAD, OPTIONS, POST,

Android開發——資料庫框架Suger遇到的大坑(GsonSuger的複用Bean請見“大坑三”)

Android開發——資料庫框架Suger遇到的大坑(Gson和Suger的複用Bean請見“大坑三”) 大坑一 自己寫了一個Demo按照官網以及GitHub上Suger的使用步驟完整走下來,Demo閃退,以插入資料為例 User user = new User("liao",

教你如何構建非同步伺服器客戶端的 Kotlin 框架 Ktor

Ktor 是一個使用 Kotlin 以最小的成本快速建立 Web 應用程式的框架。 Ktor 是一個用於在連線系統(connected systems)中構建非同步伺服器和客戶端的 Kotlin 框架。它由 Kotlin 團隊建立,因此,它充分利用了 Kotlin 的語言特性,為開發者提供出色的體驗和執

反射註解動態代理

反射是指計算機程式在執行時訪問、檢測和修改它本身狀態或行為的一種能力,是一種超程式設計語言特性,有很多語言都提供了對反射機制的支援,它使程式能夠編寫程式。Java的反射機制使得Java能夠動態的獲取類的資訊和呼叫物件的方法。 一、Java反射機制及基本用法 在Java中,Class(類型別)是反射程式設計的起

greenDao 資料庫框架簡單使用Fresco 簡單載入圖片

首先 在專案下 -----------在repositories的括號里加入倉庫---- mavenCentral() ------------- 在dependencies的括號里加入外掛--------- classpath ‘org.greenrobot:

應用SuperIO(SIO)開源跨平臺物聯網框架ServerSuperIO(SSIO)構建系統的整體方案

SSIO的更新       在SSIO上增加了UDP通訊方式,可以到Github上下載原始碼。在原來的專案中,遠端的裝置與中心站的資料互動並沒有使用過UDP方式。這種短連線的通訊鏈路,不容易維護,主要體現在:(1)持續的資料互動能力。(2)對現場裝置進行長時間的維護和校準。(3)SSIO要協調裝置、

《物聯網框架ServerSuperIO教程》- 23.動態資料介面增加快取,提高資料輸出到OPCServer(實時)資料庫的效率

 22.1   概述及要解決的問題       裝置驅動有DeviceDynamic介面,可以繼承並增加新的實時資料屬性,每次通訊完成後更新這些屬性資料。原來是通過DeviceDynamic介面實體類反射的方式獲得最新的實時資料,並輸出到關係資料庫、實時資料庫和OPC Server等介面。      但是

java泛型詳解反射泛型通用BaseDao實現

一 泛型的基本概念 1.1什麼是泛型? 泛型,即“引數化型別”。一提到引數,最熟悉的就是定義方法時有形參,然後呼叫此方法時傳遞實參。那麼引數化型別怎麼理解呢?顧名思義,就是將型別由原來的具體的型別引數化,類似於方法中的變數引數,此時型別也定義成引數形式(可以

java語言基礎--列舉,註解,正則反射

註解 @Retention(RetentionPolicy.RUNTIME)//註解保留策略 public @interface MyAnno { String str(); int val(); } @MyAnno(str = "測試註解",val = 100)

java語言基礎--枚舉,註解,正則反射

ota 獲取 -- tty ide 並不是 system ret 測試 註解 @Retention(RetentionPolicy.RUNTIME)//註解保留策略 public @interface MyAnno { String str(); int

通用匯入工具類,通過poi反射實現

將檔案從前端上傳到後端,就不說了,我的文章裡面有上傳的,剛開始寫,如果有問題歡迎批評指正首先,先說一下用法,因為之前每增加一個匯入模板就要單獨開發,比較浪費時間,所以單獨搞了個通用類,引數有待解析檔案,輸出的類,欄位陣列,開始行數,然後返回一個List1.我們已經獲取到這個檔