在mybatis中,可以通過註解@Select(sql語句)對接口進行註釋,並且實現開發,那麼,如何自己實現呢?
首先需要對註解和反射機制具有一定的瞭解。這些屬於基礎知識,這裏不贅述。
一、測試項目目錄結構
使用的測試數據庫是Oracle數據庫,使用的表是scott用戶下的dept表。(這裏使用什麼數據庫並沒有什麼意義)
Dept.java:
這是用來保存實體數據的類,查詢的數據最終會返回成一個Dept實體類的對象列表
JDBCUtil.java:
這裏封裝了JDBC,並且使用反射機制對sql的返回結果進行處理,從而生成實體類。
MapperTest.java:
相當於mybatis中的mapper接口
@param.java:
註解,用於註釋方法參數,這裏並沒有使用到
@Select.java
註解,用於註解mapper接口,代表一個sql查詢語句
conf.properties:
保存數據庫的連接信息。
二、JDBC的封裝
1)封裝jdbc連接
1.加載驅動和連接屬性
private static Properties pro = new Properties();
private static Connection conn;
//在靜態代碼塊中加載jdbc驅動包,因爲這件事情在程序的運行過程中只需要執行一次
//加載Properties文件,用於讀取
static {
try {
pro.load(new FileInputStream("src/conf.properties"));
Class.forName("oracle.jdbc.OracleDriver");
} catch (ClassNotFoundException e) {
System.out.println("驅動文件錯誤");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
2.獲取一個連接
//獲取連接對象
public static Connection getConnection() {
try {
//爲了高可用,只有當連接還未連接,或者連接已經關閉了,才重新創建連接
if (conn == null || conn.isClosed()) {
conn = DriverManager.getConnection(pro.getProperty("url"),
pro.getProperty("user"), pro.getProperty("password"));
} else
;
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
3.封裝查詢
// 查詢結果並且返回一個對象數組
public static List<Object> QueryResultBySQL(Class<?> T, String sql,
Object... params) {
List<Object> list = new ArrayList<Object>();
PreparedStatement prsm = null;
try {
prsm = getConnection().prepareStatement(sql);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
//這裏爲了省事,封裝了一個方法用來給預編譯sql的?賦值
setValues(prsm, params);
try {
prsm.executeQuery();
ResultSet rs = prsm.getResultSet();
Method[] methods = T.getDeclaredMethods();
while (rs.next()) {
//處理查詢結果,這裏寫的比較粗糙,但是能用
Object obj = T.getConstructors()[0].newInstance();
list.add(obj);
for (Method method : methods) {
if ("set"
.equalsIgnoreCase(method.getName().substring(0, 3))) {
Object o = rs.getObject(method.getName().substring(3));
if (o instanceof java.math.BigDecimal) {
BigDecimal number = (BigDecimal) o;
method.invoke(obj, number.intValue());
} else {
method.invoke(obj, o);
}
}
}
}
return list;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
//用於給預編譯sql語句賦值
public static void setValues(PreparedStatement ps, Object[] objs) {
if(objs!=null)
for (int i = 0; i < objs.length; i++) {
try {
ps.setObject(i + 1, objs[i]);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2)編寫註解
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
//只有元註解Retention的值是Runtime註解才能在運行時被加載
@Retention(RUNTIME)
//Target的值是Method表示這個註解只能用來修飾方法
@Target(METHOD)
public @interface Select {
String value();
}
3)編寫自己的Mapper
public interface MapperTest {
@Select("select * from dept where deptno = ${deptno} and dname = #{dename}")
public Dept selectEmpById(@param String deptno,@param String dname);
}
4)Dept實體類
public class Dept {
private int deptno;
private String dname;
private String loc;
@Override
public String toString() {
return "Dept [deptno=" + deptno + ", dname=" + dname + ", loc=" + loc
+ "]";
}
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
}
5)編寫測試類,使用反射機制來運行Mapper
package com.mybatis.demo1;
import java.lang.reflect.Method;
import java.util.List;
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
// 獲取mapper類對象 ,異常拋出,不處理
Class mapper = Class.forName("com.mybatis.demo1.MapperTest");
// Map<Method,String> methodMap = new HashMap<Method,String>();
// 獲取mapper的方法
Method[] methods = mapper.getMethods();
// 遍歷方法
for (Method method : methods) {
// 取出被select修飾的方法
if (method.isAnnotationPresent(Select.class)) {
// 獲取註解的內容
String sql = method.getAnnotation(Select.class).value();
// 假定兩個值
String deptno = "10";
String dname = "ACCOUNTING";
// 現在把${}修飾的改成 ?,把#{} 修飾的改成常量
for (int index = 0; index < sql.length(); index++) {
if (sql.charAt(index) == '$') {
if (sql.charAt(index + 1) == '{') {
for (int i = index; i < sql.length(); i++) {
if (sql.charAt(i) == '}') {
// 修改成?
String param = sql.substring(index, i+1);
System.out.println("修改"+param+"改成?");
sql = sql.substring(0, index) + "?" + sql.substring(i+1);
System.out.println(sql);
break;
}
}
}
} else if (sql.charAt(index) == '#') {
if (sql.charAt(index + 1) == '{') {
for (int i = index; i < sql.length(); i++) {
if (sql.charAt(i) == '}') {
// 修改成常量
String param = sql.substring(index, i+1);
System.out.println("修改"+param+"改成'常量值'");
sql = sql.substring(0, index) +" '" +dname +"'"+ sql.substring(i+1);
System.out.println(sql);
break;
}
}
}
}
}
System.out.println("最終的SQL語句:\n"+sql);
// 執行sql語句並且打印結果
List l = JDBCUtil.QueryResultBySQL(com.mybatis.demo1.Dept.class, sql, deptno);
System.out.println("查詢的結果:");
for (Object o : l) {
System.out.println(o);
}
}
}
}
}
三、最後的運行結果: