免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
mybatis的本質(zhì)和原理

背景

    項(xiàng)目需要,我們需要自己做一套mybatis,或者使用大部分mybatis的原始內(nèi)容。對(duì)其改造,以適應(yīng)需要。這就要求我再次學(xué)習(xí)一下mybatis,對(duì)它有更深入的了解。

是什么
    MyBatis是一個(gè)持久層框架,用來(lái)處理對(duì)象關(guān)系映射。說(shuō)白了就是以相對(duì)面向?qū)ο蟮姆绞絹?lái)提交sql語(yǔ)句給jdbc。如果想找個(gè)簡(jiǎn)單、快速上手的例子,最好是和spring想結(jié)合的。直接用官網(wǎng)的吧,簡(jiǎn)單清晰也沒(méi)誰(shuí)了:http://mybatis.org/spring/getting-started.html

https://mybatis.org/mybatis-3/getting-started.html

為什么

    Java開(kāi)發(fā)都是面向?qū)ο蟮乃季S,如果用傳統(tǒng)下面自己去調(diào)用連接拼裝sql的方式,維護(hù)成本高,代碼可讀性差。

public static void main(String[] args) {
//數(shù)據(jù)庫(kù)連對(duì)
Connection conn = null;
//數(shù)據(jù)庫(kù)操作對(duì)
PreparedStatement stmt = null;
//1、加載驅(qū)動(dòng)程序
try {
Class.forName(DBDRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//2、接數(shù)據(jù)庫(kù)
//過(guò)連接管理器接數(shù)據(jù)庫(kù)
try {
//接的時(shí)候直接入用戶(hù)名和密才可以
conn = DriverManager.getConnection(DBURL, USERNAME, PASSWORD);
} catch (SQLException e) {
e.printStackTrace();
}
//3、向數(shù)據(jù)庫(kù)中插入一條數(shù)據(jù)
String sql = "INSERT INTO person(name,age) VALUES (?,?)";
try {
stmt = conn.prepareStatement(sql);
stmt.setString(1,"");
stmt.setInt(2,21);
stmt.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
//4、執(zhí)語(yǔ)
try {
ResultSet resultSet = stmt.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
//5、關(guān)操作,步相反哈~
try {
stmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

怎么做

    我們來(lái)看一下底層是怎么處理和交互的。基本流程如下:

    看著頭大?沒(méi)事,我們先從最簡(jiǎn)化的版本開(kāi)始添枝加葉。MyBatis可以用配置文件或者注解形式注入sql。因?yàn)榕渲梦募绞娇梢苑奖愕奶幚韯?dòng)態(tài)SQL(動(dòng)態(tài)SQL就是sql語(yǔ)句里有if else for這些的,可以根據(jù)參數(shù)的變化最終sql也跟著變化)等優(yōu)點(diǎn),用的更為普遍。

    假設(shè)現(xiàn)在是2000年,Clinton Begin還沒(méi)有發(fā)起ibatis(mybatis的前身)項(xiàng)目。而apache基金會(huì)內(nèi)部發(fā)起了討論要設(shè)計(jì)這樣一個(gè)產(chǎn)品,指派你作為項(xiàng)目負(fù)責(zé)人?,F(xiàn)在思考,你的思路是什么?

    一般思路是先把架構(gòu)搭建起來(lái),做成一個(gè)MVP最小可行性版本,然后再做功能增強(qiáng)。

    從功能最簡(jiǎn)化方面來(lái)看,需要兩步:第一步要將sql及所需要的元素以對(duì)象的形式輸入,第二步是獲取到這些信息轉(zhuǎn)換成jdbc信息處理。

    這樣拆解后的思路是將sql及所需要的元素拆解成類(lèi)方法的參數(shù)形式,方法本身要做的事情就是將這些參數(shù)以jdbc編程需要的形式傳給jdbc執(zhí)行。這里方法內(nèi)部做的事情是一樣的,那就自然而然的想到不用每個(gè)類(lèi)都有一個(gè)實(shí)現(xiàn)。只要定義好接口,把實(shí)現(xiàn)用代理或者上層切面的方式統(tǒng)一處理就可以了。

    根據(jù)這個(gè)思路,首先要用代理來(lái)獲取參數(shù)。我設(shè)計(jì)使用方式是Insert、Select等注解里寫(xiě)sql元語(yǔ)句。通過(guò)方法參數(shù)注入?yún)?shù)。最終返回結(jié)果。如下

public interface UserMapper {
    @Insert("INSERT INTO person(name,age) VALUES (#{name},#{age})")
Integer insertUser(User user);
}

    要實(shí)現(xiàn)接口的解析。先建立一個(gè)類(lèi),里面構(gòu)造一個(gè)代理類(lèi),實(shí)現(xiàn)類(lèi)似于SqlSession,所以起名叫YunaSession(yuna是我給經(jīng)典java學(xué)習(xí)場(chǎng)景工程https://github.com/xiexiaojing/yuna 起的名字)

public class YunaSession {
public static Object dealSql(Class clazz) {
Class c[] = new Class[]{clazz};

return Proxy.newProxyInstance(YunaSession.class.getClassLoader(), c,

new YunaInvocationHandler());

}
}

    下面要實(shí)現(xiàn)的是代理中YunaInvocationHandler真正要實(shí)現(xiàn)的邏輯:將這些參數(shù)以jdbc編程需要的形式傳給jdbc執(zhí)行。也就是說(shuō)把上面【為什么】部分一開(kāi)始的那段執(zhí)行jdbc的代碼貼進(jìn)去,將sql和參數(shù)的部分做替換。

     我們把關(guān)鍵再貼一遍便于說(shuō)明問(wèn)題

//3、向數(shù)據(jù)庫(kù)中插入一條數(shù)據(jù)
String sql = "INSERT INTO person(name,age) VALUES (?,?)";
try {
stmt = conn.prepareStatement(sql);
stmt.setString(1,"");
stmt.setInt(2,21);
stmt.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}

    這里有兩個(gè)?,而jdbc的預(yù)處理語(yǔ)句傳入?yún)?shù)的時(shí)候要明確的知道第一個(gè)參數(shù)的類(lèi)型是什么,如果傳過(guò)來(lái)是對(duì)象的話(huà),要知道對(duì)應(yīng)對(duì)象的哪個(gè)值。這就是為什么接口里的預(yù)處理語(yǔ)句傳入是

INSERT INTO person(name,age) VALUES (#{name},#{age})

    因?yàn)榭梢酝ㄟ^(guò)匹配#{XX}這樣的確定都是哪些參數(shù),因?yàn)閁ser對(duì)象里有定義參數(shù)的類(lèi)型。所以類(lèi)型和值都確定了。這個(gè)就是MappedStatement對(duì)象做的事情。以下是用正則表達(dá)式匹配+反射來(lái)達(dá)到解析sql并和對(duì)象值做匹配的實(shí)現(xiàn):

public static void main(String[] args) throws Exception{
Matcher m= pattern.matcher("INSERT INTO person(name,age) VALUES (#{name},#{age})");
User user1 = new User();
user1.setId(1);
user1.setName("元春");
user1.setAge(27);
int i=1;
while(m.find()) {
System.out.println(m.group());
String group = m.group();
String fieldName = group.replace("#{","").replace("}","");
Field field = User.class.getDeclaredField(fieldName);
field.setAccessible(true);
if("java.lang.Integer".equals(field.getType().getName())) {
System.out.println("stmt.setInt("+i+","+field.get(user1)+")");
} else if("java.lang.String".equals(field.getType().getName())) {
System.out.println(" stmt.setString("+i+","+field.get(user1)+")");
}
i++;
}
}

運(yùn)行結(jié)果是

可以看到實(shí)現(xiàn)了效果。下面就是和jdbc連接結(jié)合起來(lái)。

public class YunaInvocationHandler implements InvocationHandler {
public static final String DBDRIVER = "org.xx.mm.mysql.Driver";
public static final String DBURL = "jdbc:mysql://localhost:3306/mydb";
//現(xiàn)在使用的是mysql數(shù)據(jù)庫(kù),是直接接的,所以此有用戶(hù)名和密
public static final String USERNAME = "root";
public static final String PASSWORD = "mysqladmin";
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Exception{
Object result = null;
Insert insert = method.getAnnotation(Insert.class);
if (insert != null) {
String sql = insert.value()[0];
System.out.println("插入語(yǔ)"+s);
YunaSqlDeal yunaSqlDeal = new YunaSqlDeal();
yunaSqlDeal.insert(s, Arrays.toString(args));
//1、加載驅(qū)動(dòng)程序
try {
Class.forName(DBDRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//2、接數(shù)據(jù)庫(kù)
//過(guò)連接管理器接數(shù)據(jù)庫(kù)
//數(shù)據(jù)庫(kù)連對(duì)
Connection conn = null;
try {
//接的時(shí)候直接入用戶(hù)名和密才可以
conn = DriverManager.getConnection(DBURL, USERNAME, PASSWORD);
} catch (SQLException e) {
e.printStackTrace();
}
composeStatement(sql, args[0], conn);
}
return 1;
}
private static final String PATTERN = "#\\{[A-Za-z0-9]+\\}";
private static Pattern pattern = Pattern.compile("("+PATTERN+")");
public static void composeStatement(String sql, Object obj, Connection conn) throws Exception{
PreparedStatement stmt = conn.prepareStatement(sql.replaceAll(PATTERN, ""));
Matcher m= pattern.matcher(sql);
int i=1;
while(m.find()) {
System.out.println(m.group());
String group = m.group();
String fieldName = group.replace("#{","").replace("}","");
Field field = User.class.getDeclaredField(fieldName);
field.setAccessible(true);
if("java.lang.Integer".equals(field.getType().getName())) {
System.out.println("stmt.setInt("+i+","+field.get(obj)+")");
stmt.setInt(i, Integer.parseInt(field.get(obj).toString()));
} else if("java.lang.String".equals(field.getType().getName())) {
stmt.setString(i, field.get(obj).toString());
}
i++;
}
stmt.execute();
stmt.close();
conn.close();
}
}

    這個(gè)實(shí)現(xiàn)的是insert的,返回值類(lèi)型固定,如果是select查詢(xún)語(yǔ)句,涉及到返回的結(jié)果封裝成對(duì)象。思路也是通過(guò)反射,和參數(shù)轉(zhuǎn)換步驟差不多,就不貼代碼了。

    到此,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)化版的mybatis框架。比貼的架構(gòu)圖簡(jiǎn)化在少用了很多設(shè)計(jì)模式的東西,和出于性能考慮重用的東西。mybatis的核心就實(shí)現(xiàn)完了。

總結(jié)

    本文從mybatis的設(shè)計(jì)者角度出發(fā),構(gòu)造了一個(gè)簡(jiǎn)化的mybatis框架。具體可運(yùn)行的完整代碼放到了我的github上,地址:

https://github.com/xiexiaojing/yuna。

    很多原理性的東西看過(guò)之后會(huì)忘,但是如果真正站在設(shè)計(jì)者角度實(shí)現(xiàn)過(guò)一個(gè)簡(jiǎn)化的版本,相信會(huì)增強(qiáng)記憶。同時(shí)也能和真正的實(shí)現(xiàn)做對(duì)比,更深層學(xué)習(xí)技術(shù)大牛們的設(shè)計(jì)精華。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶(hù)發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Java的jdbc使用addBatch進(jìn)行批處理操作的幾種方式
(1) Java實(shí)現(xiàn)JDBC連接及事務(wù)的方式
jdbc連接數(shù)據(jù)庫(kù)
在處理jsp讀取mysql中遇到的問(wèn)題記錄
JAVA 連接數(shù)據(jù)庫(kù)的步驟
java swing jdbc sql 增刪改查 實(shí)例1
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服