日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

Java软件开发:自定义MyBatis持久层框架

發(fā)布時(shí)間:2024/10/8 java 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java软件开发:自定义MyBatis持久层框架 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

自定義MyBatis持久層框架

  • 1 框架概述
    • 1.1 什么是框架
    • 1.2 框架要解決的問題
    • 1.3 軟件開發(fā)的分層的重要性
  • 2 MyBatis框架
  • 3 JDBC編程
    • 3.1 JDBC程序的回顧
    • 3.2 JDBC存在的問題
  • 4 搭建MyBatis開發(fā)環(huán)境
    • 4.1 創(chuàng)建Maven工程
    • 4.2 編寫實(shí)體類
    • 4.3 編寫持久層接口
    • 4.4 編寫持久層接口的映射文件
    • 4.5 編寫 SqlMapConfig 配置文件
    • 4.6 編寫測(cè)試類
  • 5 三種常用設(shè)計(jì)模式
    • 5.1 工廠模式
    • 5.2 代理模式
    • 5.3 建造者模式
  • 6 自定義MyBatis框架
    • 6.1 創(chuàng)建Maven工程
    • 6.2 引入工具類
    • 6.3 編寫讀取配置文件類
    • 6.4 編寫Mapper類
    • 6.5 編寫 Configuration 配置類
    • 6.6 編寫構(gòu)建者類
    • 6.7 編寫 SqlSessionFactory 接口和實(shí)現(xiàn)類
    • 6.8 編寫 SqlSession 接口和實(shí)現(xiàn)類
    • 6.9 編寫用于創(chuàng)建 Dao 接口代理對(duì)象的類

1 框架概述

1.1 什么是框架

框架(Framework)是整個(gè)或部分系統(tǒng)的可重用設(shè)計(jì),表現(xiàn)為一組抽象構(gòu)件及構(gòu)件實(shí)例間交互的方法;另一種定義認(rèn)為,框架是可被應(yīng)用開發(fā)者定制的應(yīng)用骨架。前者是從應(yīng)用方面,而后者是從目的方面給出的定義。

簡(jiǎn)而言之,框架其實(shí)就是某種應(yīng)用的半成品,就是一組組件,供你選用完成你自己的系統(tǒng)。簡(jiǎn)單說(shuō)就是使用別人搭好的舞臺(tái),你來(lái)做表演。而且,框架一般是成熟的,不斷升級(jí)的軟件。

1.2 框架要解決的問題

框架要解決的最重要的一個(gè)問題是技術(shù)整合的問題,在 J2EE 的框架中,有著各種各樣的技術(shù),不同的軟件企業(yè)需要從 J2EE 中選擇不同的技術(shù),這就使得軟件企業(yè)最終的應(yīng)用依賴于這些技術(shù),技術(shù)自身的復(fù)雜性和技術(shù)的風(fēng)險(xiǎn)性將會(huì)直接對(duì)應(yīng)用造成沖擊。

而應(yīng)用是軟件企業(yè)的核心,是競(jìng)爭(zhēng)力的關(guān)鍵所在,因此應(yīng)該將應(yīng)用自身的設(shè)計(jì)和具體的實(shí)現(xiàn)技術(shù)解耦。這樣,軟件企業(yè)的研發(fā)將集中在應(yīng)用的設(shè)計(jì)上,而不是具體的技術(shù)實(shí)現(xiàn),技術(shù)實(shí)現(xiàn)是應(yīng)用的底層支撐,它不應(yīng)該直接對(duì)應(yīng)用產(chǎn)生影響。

框架一般處在低層應(yīng)用平臺(tái)(如 J2EE)和高層業(yè)務(wù)邏輯之間的中間層。

1.3 軟件開發(fā)的分層的重要性

框架的重要性在于它實(shí)現(xiàn)了部分功能,并且能夠很好的將低層應(yīng)用平臺(tái)和高層業(yè)務(wù)邏輯進(jìn)行了緩和。為了實(shí)現(xiàn)軟件工程中的“高內(nèi)聚、低耦合”。把問題劃分開來(lái)各個(gè)解決,易于控制,易于延展,易于分配資源。我們常見的MVC 軟件設(shè)計(jì)思想就是很好的分層思想。


通過分層更好的實(shí)現(xiàn)了各個(gè)部分的職責(zé),在每一層將再細(xì)化出不同的框架,分別解決各層關(guān)注的問題。

2 MyBatis框架

mybatis 是一個(gè)優(yōu)秀的基于 java 的持久層框架,它內(nèi)部封裝了 jdbc,使開發(fā)者只需要關(guān)注 sql 語(yǔ)句本身,而不需要花費(fèi)精力去處理加載驅(qū)動(dòng)、創(chuàng)建連接、創(chuàng)建 statement 等繁雜的過程。

mybatis 通過 xml 或注解的方式將要執(zhí)行的各種 statement 配置起來(lái),并通過 java 對(duì)象和 statement 中sql 的動(dòng)態(tài)參數(shù)進(jìn)行映射生成最終執(zhí)行的 sql 語(yǔ)句,最后由 mybatis 框架執(zhí)行 sql 并將結(jié)果映射為 java 對(duì)象并返回。

采用 ORM 思想解決了實(shí)體和數(shù)據(jù)庫(kù)映射的問題,對(duì) jdbc 進(jìn)行了封裝,屏蔽了 jdbc api 底層訪問細(xì)節(jié),使我們不用與 jdbc api 打交道,就可以完成對(duì)數(shù)據(jù)庫(kù)的持久化操作。

3 JDBC編程

3.1 JDBC程序的回顧

import java.sql.*;public class Main {public static void main(String[] args) {Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;String url = "jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf-8";String driver = "com.mysql.jdbc.Driver";String username = "root";String password = "Root@2020";try {//加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)Class.forName(driver);//通過驅(qū)動(dòng)管理類獲取數(shù)據(jù)庫(kù)連接connection = DriverManager.getConnection(url, username, password);//定義Sql語(yǔ)句,?標(biāo)識(shí)占位符String sql = "select * from user where username = ?";//獲取預(yù)處理statementpreparedStatement = connection.prepareStatement(sql);//設(shè)置參數(shù),第一個(gè)參數(shù)是sql語(yǔ)句中參數(shù)的序號(hào)(從1開始),第二個(gè)參數(shù)設(shè)置的是參數(shù)值preparedStatement.setString(1, "老王");//數(shù)據(jù)庫(kù)執(zhí)行查詢,返回查詢結(jié)果集resultSet = preparedStatement.executeQuery();//遍歷查詢結(jié)果集while (resultSet.next()) {System.out.println(resultSet.getString("id") + "," + resultSet.getString("username"));}} catch (Exception e) {e.printStackTrace();} finally {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();}}}} }

3.2 JDBC存在的問題

1、數(shù)據(jù)庫(kù)鏈接創(chuàng)建、釋放頻繁造成系統(tǒng)資源浪費(fèi)從而影響系統(tǒng)性能,如果使用數(shù)據(jù)庫(kù)連接池可解決此問題。

2、Sql 語(yǔ)句在代碼中硬編碼,造成代碼不易維護(hù),實(shí)際應(yīng)用 sql 變化的可能較大,sql 變動(dòng)需要改變 java代碼。

3、使用 preparedStatement 向占有位符號(hào)傳參數(shù)存在硬編碼,因?yàn)?sql 語(yǔ)句的 where 條件不一定,可能多也可能少,修改 sql 還要修改代碼,系統(tǒng)不易維護(hù)。

4、對(duì)結(jié)果集解析存在硬編碼(查詢列名),sql 變化導(dǎo)致解析代碼變化,系統(tǒng)不易維護(hù),如果能將數(shù)據(jù)庫(kù)記錄封裝成 pojo 對(duì)象解析比較方便。

4 搭建MyBatis開發(fā)環(huán)境

4.1 創(chuàng)建Maven工程

<dependencies><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.5</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.12</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.10</version><scope>test</scope></dependency></dependencies>

4.2 編寫實(shí)體類

public class User implements Serializable {private Integer id;private String username;private Date birthday;private String sex;private String address;@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", birthday=" + birthday +", sex='" + sex + '\'' +", address='" + address + '\'' +'}';}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;} }

4.3 編寫持久層接口

public interface IUserDao {/*** 查詢所有* @return*/List<User> findAll(); }

4.4 編寫持久層接口的映射文件

創(chuàng)建位置:必須和持久層接口在相同的包中。
名稱:必須以持久層接口名稱命名文件名,擴(kuò)展名是.xml

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.baidu.dao.IUserDao"> <!-- 配置查詢所有--><select id="findAll" resultType="com.company.domain.User">select * from user</select> </mapper>

4.5 編寫 SqlMapConfig 配置文件

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration><!--配置環(huán)境--><environments default="mysql"><!--配置mysql環(huán)境--><environment id="mysql"><!--配置事務(wù)類型--><transactionManager type="JDBC"></transactionManager><!--配置數(shù)據(jù)源(連接池)--><dataSource type="POOLED"><!--配置連接數(shù)據(jù)庫(kù)的四個(gè)基本信息--><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatisdb"/><property name="username" value="root"/><property name="password" value="Root@2020"/></dataSource></environment></environments><!--指定映射配置文件的位置--><mappers><mapper resource="com/company/dao/IUserDao.xml"/></mappers> </configuration>

4.6 編寫測(cè)試類

public class MybatisTest {public static void main(String[] args) throws Exception {//1.讀取配置文件InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");//2.創(chuàng)建SqlSessionFactory工廠SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();SqlSessionFactory factory = builder.build(in);//3.使用工廠生產(chǎn)SqlSession對(duì)象SqlSession session = factory.openSession();//4.使用SqlSession創(chuàng)建DAO的代理對(duì)象IUserDao userDao = session.getMapper(IUserDao.class);//5.使用代理對(duì)象執(zhí)行方法List<User> users = userDao.findAll();for (User user : users) {System.out.println(user);}//6.釋放資源session.close();in.close();} }

5 三種常用設(shè)計(jì)模式

5.1 工廠模式

工廠模式(Factory Pattern)是 Java 中最常用的設(shè)計(jì)模式之一。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式。

在工廠模式中,我們?cè)趧?chuàng)建對(duì)象時(shí)不會(huì)對(duì)客戶端暴露創(chuàng)建邏輯,并且是通過使用一個(gè)共同的接口來(lái)指向新創(chuàng)建的對(duì)象。

意圖:定義一個(gè)創(chuàng)建對(duì)象的接口,讓其子類自己決定實(shí)例化哪一個(gè)工廠類,工廠模式使其創(chuàng)建過程延遲到子類進(jìn)行。

主要解決:主要解決接口選擇的問題。

何時(shí)使用:我們明確地計(jì)劃不同條件下創(chuàng)建不同實(shí)例時(shí)。

如何解決:讓其子類實(shí)現(xiàn)工廠接口,返回的也是一個(gè)抽象的產(chǎn)品。

關(guān)鍵代碼:創(chuàng)建過程在其子類執(zhí)行。

5.2 代理模式

在代理模式(Proxy Pattern)中,一個(gè)類代表另一個(gè)類的功能。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式。

在代理模式中,我們創(chuàng)建具有現(xiàn)有對(duì)象的對(duì)象,以便向外界提供功能接口。

意圖:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問。

主要解決:在直接訪問對(duì)象時(shí)帶來(lái)的問題,比如說(shuō):要訪問的對(duì)象在遠(yuǎn)程的機(jī)器上。在面向?qū)ο笙到y(tǒng)中,有些對(duì)象由于某些原因(比如對(duì)象創(chuàng)建開銷很大,或者某些操作需要安全控制,或者需要進(jìn)程外的訪問),直接訪問會(huì)給使用者或者系統(tǒng)結(jié)構(gòu)帶來(lái)很多麻煩,我們可以在訪問此對(duì)象時(shí)加上一個(gè)對(duì)此對(duì)象的訪問層。

何時(shí)使用:想在訪問一個(gè)類時(shí)做一些控制。

如何解決:增加中間層。

關(guān)鍵代碼:實(shí)現(xiàn)與被代理類組合。

5.3 建造者模式

建造者模式(Builder Pattern)使用多個(gè)簡(jiǎn)單的對(duì)象一步一步構(gòu)建成一個(gè)復(fù)雜的對(duì)象。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式。

一個(gè) Builder 類會(huì)一步一步構(gòu)造最終的對(duì)象。該 Builder 類是獨(dú)立于其他對(duì)象的。

意圖:將一個(gè)復(fù)雜的構(gòu)建與其表示相分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。

主要解決:主要解決在軟件系統(tǒng)中,有時(shí)候面臨著"一個(gè)復(fù)雜對(duì)象"的創(chuàng)建工作,其通常由各個(gè)部分的子對(duì)象用一定的算法構(gòu)成;由于需求的變化,這個(gè)復(fù)雜對(duì)象的各個(gè)部分經(jīng)常面臨著劇烈的變化,但是將它們組合在一起的算法卻相對(duì)穩(wěn)定。

何時(shí)使用:一些基本部件不會(huì)變,而其組合經(jīng)常變化的時(shí)候。

如何解決:將變與不變分離開。

關(guān)鍵代碼:建造者:創(chuàng)建和提供實(shí)例,導(dǎo)演:管理建造出來(lái)的實(shí)例的依賴關(guān)系。

6 自定義MyBatis框架

6.1 創(chuàng)建Maven工程

<dependencies> <!-- <dependency>--> <!-- <groupId>org.mybatis</groupId>--> <!-- <artifactId>mybatis</artifactId>--> <!-- <version>3.4.5</version>--> <!-- </dependency>--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.12</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.10</version><scope>test</scope></dependency><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version></dependency><dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.1.6</version></dependency></dependencies>

6.2 引入工具類

public class XMLConfigBuilder {/*** 解析主配置文件,把里面的內(nèi)容填充到DefaultSqlSession所需要的地方* 使用的技術(shù):* dom4j+xpath*/public static Configuration loadConfiguration(InputStream config) {try {//定義封裝連接信息的配置對(duì)象(mybatis的配置對(duì)象)Configuration cfg = new Configuration();//1.獲取SAXReader對(duì)象SAXReader reader = new SAXReader();//2.根據(jù)字節(jié)輸入流獲取Document對(duì)象Document document = reader.read(config);//3.獲取根節(jié)點(diǎn)Element root = document.getRootElement();//4.使用xpath中選擇指定節(jié)點(diǎn)的方式,獲取所有property節(jié)點(diǎn)List<Element> propertyElements = root.selectNodes("//property");//5.遍歷節(jié)點(diǎn)for (Element propertyElement : propertyElements) {//判斷節(jié)點(diǎn)是連接數(shù)據(jù)庫(kù)的哪部分信息//取出name屬性的值String name = propertyElement.attributeValue("name");if ("driver".equals(name)) {//表示驅(qū)動(dòng)//獲取property標(biāo)簽value屬性的值String driver = propertyElement.attributeValue("value");cfg.setDriver(driver);}if ("url".equals(name)) {//表示連接字符串//獲取property標(biāo)簽value屬性的值String url = propertyElement.attributeValue("value");cfg.setUrl(url);}if ("username".equals(name)) {//表示用戶名//獲取property標(biāo)簽value屬性的值String username = propertyElement.attributeValue("value");cfg.setUsername(username);}if ("password".equals(name)) {//表示密碼//獲取property標(biāo)簽value屬性的值String password = propertyElement.attributeValue("value");cfg.setPassword(password);}}//取出mappers中的所有mapper標(biāo)簽,判斷他們使用了resource還是class屬性List<Element> mapperElements = root.selectNodes("//mappers/mapper");//遍歷集合for (Element mapperElement : mapperElements) {//判斷mapperElement使用的是哪個(gè)屬性Attribute attribute = mapperElement.attribute("resource");if (attribute != null) {System.out.println("使用的是XML");//表示有resource屬性,用的是XML//取出屬性的值String mapperPath = attribute.getValue();//獲取屬性的值"com/itheima/dao/IUserDao.xml"//把映射配置文件的內(nèi)容獲取出來(lái),封裝成一個(gè)mapMap<String, Mapper> mappers = loadMapperConfiguration(mapperPath);//給configuration中的mappers賦值cfg.setMappers(mappers);} else {System.out.println("使用的是注解");//表示沒有resource屬性,用的是注解//獲取class屬性的值String daoClassPath = mapperElement.attributeValue("class");//根據(jù)daoClassPath獲取封裝的必要信息Map<String, Mapper> mappers = loadMapperAnnotation(daoClassPath);//給configuration中的mappers賦值cfg.setMappers(mappers);}}//返回Configurationreturn cfg;} catch (Exception e) {throw new RuntimeException(e);} finally {try {config.close();} catch (Exception e) {e.printStackTrace();}}}/*** 根據(jù)傳入的參數(shù),解析XML,并且封裝到Map中** @param mapperPath 映射配置文件的位置* @return map中包含了獲取的唯一標(biāo)識(shí)(key是由dao的全限定類名和方法名組成)* 以及執(zhí)行所需的必要信息(value是一個(gè)Mapper對(duì)象,里面存放的是執(zhí)行的SQL語(yǔ)句和要封裝的實(shí)體類全限定類名)*/private static Map<String, Mapper> loadMapperConfiguration(String mapperPath) throws IOException {InputStream in = null;try {//定義返回值對(duì)象Map<String, Mapper> mappers = new HashMap<String, Mapper>();//1.根據(jù)路徑獲取字節(jié)輸入流in = Resources.getResourceAsStream(mapperPath);//2.根據(jù)字節(jié)輸入流獲取Document對(duì)象SAXReader reader = new SAXReader();Document document = reader.read(in);//3.獲取根節(jié)點(diǎn)Element root = document.getRootElement();//4.獲取根節(jié)點(diǎn)的namespace屬性取值String namespace = root.attributeValue("namespace");//是組成map中key的部分//5.獲取所有的select節(jié)點(diǎn)List<Element> selectElements = root.selectNodes("//select");//6.遍歷select節(jié)點(diǎn)集合for (Element selectElement : selectElements) {//取出id屬性的值 組成map中key的部分String id = selectElement.attributeValue("id");//取出resultType屬性的值 組成map中value的部分String resultType = selectElement.attributeValue("resultType");//取出文本內(nèi)容 組成map中value的部分String queryString = selectElement.getText();//創(chuàng)建KeyString key = namespace + "." + id;//創(chuàng)建ValueMapper mapper = new Mapper();mapper.setQueryString(queryString);mapper.setResultType(resultType);//把key和value存入mappers中mappers.put(key, mapper);}return mappers;} catch (Exception e) {throw new RuntimeException(e);} finally {in.close();}}/*** 根據(jù)傳入的參數(shù),得到dao中所有被select注解標(biāo)注的方法。* 根據(jù)方法名稱和類名,以及方法上注解value屬性的值,組成Mapper的必要信息** @param daoClassPath* @return*/private static Map<String, Mapper> loadMapperAnnotation(String daoClassPath) throws Exception {//定義返回值對(duì)象Map<String, Mapper> mappers = new HashMap<String, Mapper>();//1.得到dao接口的字節(jié)碼對(duì)象Class daoClass = Class.forName(daoClassPath);//2.得到dao接口中的方法數(shù)組Method[] methods = daoClass.getMethods();//3.遍歷Method數(shù)組for (Method method : methods) {//取出每一個(gè)方法,判斷是否有select注解boolean isAnnotated = method.isAnnotationPresent(Select.class);if (isAnnotated) {//創(chuàng)建Mapper對(duì)象Mapper mapper = new Mapper();//取出注解的value屬性值Select selectAnno = method.getAnnotation(Select.class);String queryString = selectAnno.value();mapper.setQueryString(queryString);//獲取當(dāng)前方法的返回值,還要求必須帶有泛型信息Type type = method.getGenericReturnType();//List<User>//判斷type是不是參數(shù)化的類型if (type instanceof ParameterizedType) {//強(qiáng)轉(zhuǎn)ParameterizedType ptype = (ParameterizedType) type;//得到參數(shù)化類型中的實(shí)際類型參數(shù)Type[] types = ptype.getActualTypeArguments();//取出第一個(gè)Class domainClass = (Class) types[0];//獲取domainClass的類名String resultType = domainClass.getName();//給Mapper賦值mapper.setResultType(resultType);}//組裝key的信息//獲取方法的名稱String methodName = method.getName();String className = method.getDeclaringClass().getName();String key = className + "." + methodName;//給map賦值mappers.put(key, mapper);}}return mappers;} } public class Executor {public <E> List<E> selectList(Mapper mapper, Connection conn) {PreparedStatement pstm = null;ResultSet rs = null;try {//1.取出mapper中的數(shù)據(jù)String queryString = mapper.getQueryString();//select * from userString resultType = mapper.getResultType();//com.itheima.domain.UserClass domainClass = Class.forName(resultType);//2.獲取PreparedStatement對(duì)象pstm = conn.prepareStatement(queryString);//3.執(zhí)行SQL語(yǔ)句,獲取結(jié)果集rs = pstm.executeQuery();//4.封裝結(jié)果集List<E> list = new ArrayList<E>();//定義返回值while(rs.next()) {//實(shí)例化要封裝的實(shí)體類對(duì)象E obj = (E)domainClass.newInstance();//取出結(jié)果集的元信息:ResultSetMetaDataResultSetMetaData rsmd = rs.getMetaData();//取出總列數(shù)int columnCount = rsmd.getColumnCount();//遍歷總列數(shù)for (int i = 1; i <= columnCount; i++) {//獲取每列的名稱,列名的序號(hào)是從1開始的String columnName = rsmd.getColumnName(i);//根據(jù)得到列名,獲取每列的值Object columnValue = rs.getObject(columnName);//給obj賦值:使用Java內(nèi)省機(jī)制(借助PropertyDescriptor實(shí)現(xiàn)屬性的封裝)PropertyDescriptor pd = new PropertyDescriptor(columnName,domainClass);//要求:實(shí)體類的屬性和數(shù)據(jù)庫(kù)表的列名保持一種//獲取它的寫入方法Method writeMethod = pd.getWriteMethod();//把獲取的列的值,給對(duì)象賦值writeMethod.invoke(obj,columnValue);}//把賦好值的對(duì)象加入到集合中list.add(obj);}return list;} catch (Exception e) {throw new RuntimeException(e);} finally {release(pstm,rs);}}private void release(PreparedStatement pstm,ResultSet rs){if(rs != null){try {rs.close();}catch(Exception e){e.printStackTrace();}}if(pstm != null){try {pstm.close();}catch(Exception e){e.printStackTrace();}}} } public class DataSourceUtil {public static Connection getConnection(Configuration cfg) {try {Class.forName(cfg.getDriver());return DriverManager.getConnection(cfg.getUrl(), cfg.getUsername(), cfg.getPassword());} catch (Exception e) {throw new RuntimeException(e);}} }

6.3 編寫讀取配置文件類

/*** 使用類加載器讀取配置文件*/ public class Resources {/*** 根據(jù)傳入的參數(shù),獲取一個(gè)字節(jié)輸入流** @param filepath* @return*/public static InputStream getResourceAsStream(String filepath) {return Resources.class.getClassLoader().getResourceAsStream(filepath);} }

6.4 編寫Mapper類

/*** 用于封裝執(zhí)行的SQL語(yǔ)句和結(jié)果類型的全限定類名*/ public class Mapper {private String queryString;//SQLprivate String resultType;//實(shí)體類的全限定類名public String getQueryString() {return queryString;}public void setQueryString(String queryString) {this.queryString = queryString;}public String getResultType() {return resultType;}public void setResultType(String resultType) {this.resultType = resultType;} }

6.5 編寫 Configuration 配置類

public class Configuration {private String driver;//驅(qū)動(dòng)private String url;//地址private String username;//用戶名private String password;//密碼//Map<唯一標(biāo)識(shí),Mapper>用于保存映射文件中的sql標(biāo)識(shí)及sql語(yǔ)句private Map<String, Mapper> mappers;public Map<String, Mapper> getMappers() {return mappers;}public void setMappers(Map<String, Mapper> mappers) {//此處需要使用追加的方式this.mappers = mappers;}public String getDriver() {return driver;}public void setDriver(String driver) {this.driver = driver;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;} }

6.6 編寫構(gòu)建者類

/*** 用于創(chuàng)建一個(gè)SqlSessionFactory對(duì)象*/ public class SqlSessionFactoryBuilder {public SqlSessionFactory build(InputStream in) {DefaultSqlSessionFactory factory = new DefaultSqlSessionFactory();factory.setConfig(in);return factory;} }

6.7 編寫 SqlSessionFactory 接口和實(shí)現(xiàn)類

public interface SqlSessionFactory {SqlSession openSession(); } /*** SqlSessionFactory接口的實(shí)現(xiàn)類*/ public class DefaultSqlSessionFactory implements SqlSessionFactory {private InputStream config = null;public void setConfig(InputStream config) {this.config = config;}/*** 用于創(chuàng)建一個(gè)新的操作數(shù)據(jù)庫(kù)對(duì)象** @return*/@Overridepublic SqlSession openSession() {Configuration cfg = XMLConfigBuilder.loadConfiguration(config);return new DefaultSqlSession(cfg);} }

6.8 編寫 SqlSession 接口和實(shí)現(xiàn)類

/*** 自定義Mybatis中和數(shù)據(jù)庫(kù)交互的核心類* 里面可以創(chuàng)建dao接口的代理對(duì)象*/ public interface SqlSession {/*** 根據(jù)參數(shù)創(chuàng)建一個(gè)代理對(duì)象* @param daoInterfaceClass dao的接口字節(jié)碼* @param <T>* @return*/<T> T getMapper(Class<T> daoInterfaceClass);/*** 釋放資源*/void close(); } /*** SqlSession實(shí)現(xiàn)類*/ public class DefaultSqlSession implements SqlSession {private Configuration cfg;private Connection conn;public DefaultSqlSession(Configuration cfg) {this.cfg = cfg;this.conn = DataSourceUtil.getConnection(cfg);}/*** 用于創(chuàng)建代理對(duì)象** @param daoInterfaceClass dao的接口字節(jié)碼* @param <T>* @return*/@Overridepublic <T> T getMapper(Class<T> daoInterfaceClass) {return (T) Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(),new Class[]{daoInterfaceClass},new MapperProxy(cfg.getMappers(), conn));}/*** 用于釋放資源*/@Overridepublic void close() {if (conn != null) {try {conn.close();} catch (SQLException throwables) {throwables.printStackTrace();}}}//查詢所有的方法public <E> List<E> selectList(String statement) {Mapper mapper = cfg.getMappers().get(statement);return new Executor().selectList(mapper, conn);} }

6.9 編寫用于創(chuàng)建 Dao 接口代理對(duì)象的類

public class MapperProxy implements InvocationHandler {//Map的key是全限定類名+方法名private Map<String, Mapper> mappers;private Connection conn;public MapperProxy(Map<String, Mapper> mappers, Connection conn) {this.mappers = mappers;this.conn = conn;}/*** 用于對(duì)方法進(jìn)行增強(qiáng),就是調(diào)用SelectList方法** @param proxy* @param method* @param args* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//1.獲取方法名String methodName = method.getName();//2.獲取方法所在類的名稱String className = method.getDeclaringClass().getName();//3.組合keyString key = className + "." + methodName;//4.獲取Mappers中的mapper對(duì)象Mapper mapper = mappers.get(key);//5.判斷是否有mapperif (mapper == null) {throw new IllegalArgumentException("傳入的參數(shù)有誤");}Executor executor = new Executor();//6.調(diào)用工具類查詢所有return executor.selectList(mapper,conn);} }

總結(jié)

以上是生活随笔為你收集整理的Java软件开发:自定义MyBatis持久层框架的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。