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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

mybatis 无法初始化类_从零开始手写 mybatis(一)MVP 版本

發(fā)布時(shí)間:2025/3/20 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mybatis 无法初始化类_从零开始手写 mybatis(一)MVP 版本 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

什么是 MyBatis ?

MyBatis 是一款優(yōu)秀的持久層框架,它支持定制化 SQL、存儲(chǔ)過(guò)程以及高級(jí)映射。

MyBatis 避免了幾乎所有的 JDBC 代碼和手動(dòng)設(shè)置參數(shù)以及獲取結(jié)果集。

MyBatis 可以使用簡(jiǎn)單的 XML 或注解來(lái)配置和映射原生信息,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對(duì)象)映射成數(shù)據(jù)庫(kù)中的記錄。(這是官網(wǎng)解釋)

MyBatis 運(yùn)行原理

當(dāng)框架啟動(dòng)時(shí),通過(guò)configuration解析config.xml配置文件和mapper.xml映射文件,映射文件可以使用xml方式或者注解方式,然后由configuration獲得sqlsessionfactory對(duì)象,再由sqlsessionfactory獲得sqlsession數(shù)據(jù)庫(kù)訪問(wèn)會(huì)話對(duì)象,通過(guò)會(huì)話對(duì)象獲得對(duì)應(yīng)DAO層的mapper對(duì)象,通過(guò)調(diào)用mapper對(duì)象相應(yīng)方法,框架就會(huì)自動(dòng)執(zhí)行SQL語(yǔ)句從而獲得結(jié)果。

手寫 mybatis

其實(shí)整體流程就是這么簡(jiǎn)單,我們來(lái)一起實(shí)現(xiàn)一個(gè)簡(jiǎn)單版本的 mybatis。

創(chuàng)作目的

(1)深入學(xué)習(xí) mybatis 的原理

一千個(gè)讀者就有一千個(gè)哈姆雷特,一千個(gè)作者就有一千個(gè)莎士比亞?!像R

(2)實(shí)現(xiàn)屬于自己的 mybatis 工具。

數(shù)據(jù)庫(kù)的種類實(shí)際上有幾百種,比如工作中就用到過(guò) GreenPlum 這種相對(duì)小眾的數(shù)據(jù)庫(kù),這時(shí)候 mybatis 可能就不能使用了。

感覺(jué)大可不必,符合 SQL 標(biāo)準(zhǔn)都應(yīng)該統(tǒng)一支持下,這樣更加方便實(shí)用。

實(shí)現(xiàn)方式

本系列目前共計(jì) 17 個(gè)迭代版本,基本完成了 mybatis 的核心特性。

耗時(shí)大概十天左右,相對(duì)實(shí)現(xiàn)的方式比較簡(jiǎn)單。

采用 mvp 的開(kāi)發(fā)策略,逐漸添加新的特性。

本系列將對(duì)核心代碼進(jìn)行講解,完整代碼已經(jīng)全部開(kāi)源

https://github.com/houbb/mybatis

快速體驗(yàn)

mysql 安裝

不是本系列重點(diǎn),請(qǐng)自行找資料。

版本:使用的是 v5.7 版本,v8.0 之后依賴的驅(qū)動(dòng)包會(huì)有所不同。

sql 執(zhí)行

-- auto-generated definition create table user (id int auto_incrementprimary key,name varchar(100) not null,password varchar(100) not null );insert into user (name, password) value ('ryo', '123456');

maven 引入

<dependency><groupId>com.github.houbb</groupId><artifactId>mybatis</artifactId><version>0.0.1</version> </dependency>

配置文件

  • mybatis-config-5-7.xml
<?xml version="1.0" encoding="UTF-8"?> <configuration><dataSource><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/test"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource><mappers><mapper resource="mapper/UserMapper.xml"/></mappers></configuration>

測(cè)試代碼

Config config = new XmlConfig("mybatis-config-5-7.xml");SqlSession sqlSession = new DefaultSessionFactory(config).openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.selectById(1L); System.out.println(user);

輸出結(jié)果:

User{id=1, name='ryo', password='123456'}

是不是有種 mybatis 初戀般的感覺(jué)呢?

到這里都是引子,下面我們來(lái)講述下一些核心實(shí)現(xiàn)。

代碼實(shí)現(xiàn)

maven 依賴

這里我們需要訪問(wèn) mysql,也需要解析 xml。

需要引入如下的依賴:

<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.29</version> </dependency> <dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version> </dependency>

接口定義

上述的測(cè)試代碼中,我們演示用到的幾個(gè)核心接口如下:

  • Config.java

配置接口

/*** 配置信息* @author binbin.hou* @since 0.0.1*/ public interface Config {/*** 獲取數(shù)據(jù)源信息* @return 數(shù)據(jù)源配置* @since 0.0.1*/DataSource getDataSource();/*** 獲取映射類信息* @param clazz 類信息* @return 結(jié)果* @since 0.0.1*/MapperClass getMapperData(final Class clazz);/*** 獲取映射類信息* @param clazz 類信息* @param methodName 方法名稱* @return 結(jié)果* @since 0.0.1*/MapperMethod getMapperMethod(final Class clazz,final String methodName);/*** 數(shù)據(jù)庫(kù)連接信息* @return 連接信息* @since 0.0.1*/Connection getConnection(); }
  • SqlSession.java
public interface SqlSession {/*** 查詢單個(gè)* @param mapperMethod 方法* @param args 參數(shù)* @param <T> 泛型* @return 結(jié)果* @since 0.0.1*/<T> T selectOne(final MapperMethod mapperMethod, Object[] args);/*** Retrieves a mapper.* @param <T> the mapper type* @param type Mapper interface class* @return a mapper bound to this SqlSession* @since 0.0.1*/<T> T getMapper(Class<T> type);/*** 獲取配置信息* @return 配置* @since 0.0.1*/Config getConfig();}
  • UserMapper.java

UserMapper 就是我們經(jīng)常定義的 mapper

public interface UserMapper {User selectById(final long id);}

下面我們來(lái)看看對(duì)應(yīng)的幾個(gè)比較重要的實(shí)現(xiàn)。

xml 的配置初始化

我們的很多配置放在 config.xml 文件中,肯定是通過(guò)解析 xml 實(shí)現(xiàn)的。

基礎(chǔ)屬性

public class XmlConfig extends ConfigAdaptor {/*** 文件配置路徑** @since 0.0.1*/private final String configPath;/*** 配置文件信息** @since 0.0.1*/private Element root;/*** 數(shù)據(jù)源信息** @since 0.0.1*/private DataSource dataSource;/*** mapper 注冊(cè)類** @since 0.0.1*/private final MapperRegister mapperRegister = new MapperRegister();public XmlConfig(String configPath) {this.configPath = configPath;// 配置初始化initProperties();// 初始化數(shù)據(jù)連接信息initDataSource();// mapper 信息initMapper();}@Overridepublic DataSource getDataSource() {return this.dataSource;}@Overridepublic Connection getConnection() {try {Class.forName(dataSource.driver());return DriverManager.getConnection(dataSource.url(), dataSource.username(), dataSource.password());} catch (ClassNotFoundException | SQLException e) {throw new MybatisException(e);}}@Overridepublic MapperMethod getMapperMethod(Class clazz, String methodName) {return this.mapperRegister.getMapperMethod(clazz, methodName);} }

配置初始化

這里就是解析 xml 文件的 root 節(jié)點(diǎn),便于后續(xù)使用:

root 節(jié)點(diǎn)的初始化如下:

/*** 獲取根節(jié)點(diǎn)* @param path 配置路徑* @return 元素* @since 0.0.1*/ public static Element getRoot(final String path) {try {// 初始化數(shù)據(jù)庫(kù)連接信息InputStream inputStream = StreamUtil.getInputStream(path);SAXReader reader = new SAXReader();Document document = reader.read(inputStream);return document.getRootElement();} catch (DocumentException e) {throw new MybatisException(e);} }

初始化數(shù)據(jù)連接信息

這就是解析 xml 中對(duì)于 dataSource 的配置信息:

/*** 初始化數(shù)據(jù)源** @since 0.0.1*/ private void initDataSource() {// 根據(jù)配置初始化連接信息this.dataSource = new DataSource();Element dsElem = root.element("dataSource");Map<String, String> map = new HashMap<>(4);for (Object property : dsElem.elements("property")) {Element element = (Element) property;String name = element.attributeValue("name");String value = element.attributeValue("value");map.put("jdbc." + name, value);}dataSource.username(map.get(DataSourceConst.USERNAME)).password(map.get(DataSourceConst.PASSWORD)).driver(map.get(DataSourceConst.DRIVER)).url(map.get(DataSourceConst.URL)); }

初始化 mapper

解析 xml 中的 mapper 配置。

/*** 初始化 mapper 信息** @since 0.0.1*/ private void initMapper() {Element mappers = root.element("mappers");// 遍歷所有需要初始化的 mapper 文件路徑for (Object item : mappers.elements("mapper")) {Element mapper = (Element) item;String path = mapper.attributeValue("resource");mapperRegister.addMapper(path);} }

mapperRegister 就是對(duì)方法的元數(shù)據(jù)進(jìn)行一些構(gòu)建,比如出參,入?yún)⒌念愋?#xff0c;等等,便于后期使用。

比如我們的 UserMapper.xml 方法內(nèi)容如下:

<select id = "selectById" paramType="java.lang.Long" resultType = "com.github.houbb.mybatis.domain.User">select * from user where id = ? </select>

sql 就是:select * from user where id = ?

方法標(biāo)識(shí):selectById

入?yún)?#xff1a;Long

出參:User

創(chuàng)建 session

如何創(chuàng)建

SqlSession sqlSession = new DefaultSessionFactory(config).openSession();

這句話實(shí)際執(zhí)行的是:

@Override public SqlSession openSession() {return new DefaultSqlSession(config, new SimpleExecutor()); }

獲取 mapper 實(shí)現(xiàn)

UserMapper userMapper = sqlSession.getMapper(UserMapper.class)

這里獲取 mapper,實(shí)際獲取的是什么呢?

實(shí)際上獲取到的是一個(gè)代理。

mybatis 將我們的接口,和實(shí)際 xml 中的 sql 二者通過(guò)動(dòng)態(tài)代理結(jié)合,讓我們調(diào)用 xml 中的 sql 和使用接口方法一樣自然。

獲取代理

getMapper 實(shí)際上是一個(gè)動(dòng)態(tài)代理。

@Override @SuppressWarnings("all") public <T> T getMapper(Class<T> clazz) {MapperProxy proxy = new MapperProxy(clazz, this);return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz}, proxy); }

動(dòng)態(tài)代理的實(shí)現(xiàn)

MapperProxy 的實(shí)現(xiàn)如下:

public class MapperProxy implements InvocationHandler {/*** 類信息** @since 0.0.1*/private final Class clazz;/*** sql session** @since 0.0.1*/private final SqlSession sqlSession;public MapperProxy(Class clazz, SqlSession sqlSession) {this.clazz = clazz;this.sqlSession = sqlSession;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {MapperMethod mapperMethod = this.sqlSession.getConfig().getMapperMethod(clazz, method.getName());if (mapperMethod != null) {return this.sqlSession.selectOne(mapperMethod, args);}return method.invoke(proxy, args);}}

代理了什么?

當(dāng)我們執(zhí)行 userMapper.selectById(1L) 時(shí),實(shí)際執(zhí)行的是什么?

實(shí)際執(zhí)行的是 sqlSession.selectOne(mapperMethod, args)

代理實(shí)現(xiàn)

selectOne 是比較核心的內(nèi)容了。

整體實(shí)現(xiàn)

整體如下

public <T> T query(final Config config,MapperMethod method, Object[] args) {try(Connection connection = config.getConnection();PreparedStatement preparedStatement = connection.prepareStatement(method.getSql());) {// 2. 處理參數(shù)parameterHandle(preparedStatement, args);// 3. 執(zhí)行方法preparedStatement.execute();// 4. 處理結(jié)果final Class resultType = method.getResultType();ResultSet resultSet = preparedStatement.getResultSet();ResultHandler resultHandler = new ResultHandler(resultType);Object result = resultHandler.buildResult(resultSet);return (T) result;} catch (SQLException ex) {throw new MybatisException(ex);} }

我們獲取到 xml 中的 sql,然后構(gòu)建 jdbc 中大家比較熟悉的 PreparedStatement。

然后對(duì)出參和入?yún)⑦M(jìn)行處理,最后返回結(jié)果。

入?yún)⒃O(shè)置

public void setParams(final Object[] objects) {try {for(int i = 0; i < objects.length; i++) {Object value = objects[i];// 目標(biāo)類型,這個(gè)后期可以根據(jù) jdbcType 獲取// jdbc 下標(biāo)從1開(kāi)始statement.setObject(i+1, value);}} catch (SQLException throwables) {throw new MybatisException(throwables);} }

針對(duì)我們非常簡(jiǎn)單的例子:

select * from user where id = ?

那就是直接把入?yún)⒅械?1L 設(shè)置到占位符 ? 即可。

出參處理

這里主要用到反射,將查詢結(jié)果和 javaBean 做一一映射。

/*** 構(gòu)建結(jié)果* @param resultSet 結(jié)果集合* @return 結(jié)果* @since 0.0.1*/ public Object buildResult(final ResultSet resultSet) {try {// 基本類型,非 java 對(duì)象,直接返回即可。// 可以進(jìn)行抽象Object instance = resultType.newInstance();// 結(jié)果大小的判斷// 為空直接返回,大于1則報(bào)錯(cuò)if(resultSet.next()) {List<Field> fieldList = ClassUtil.getAllFieldList(resultType);for(Field field : fieldList) {Object value = getResult(field, resultSet);ReflectFieldUtil.setValue(field, instance, value);}// 返回設(shè)置值后的結(jié)果return instance;}return null;} catch (InstantiationException | IllegalAccessException | SQLException e) {throw new MybatisException(e);} }

到這里,一個(gè)簡(jiǎn)易版的 myabtis 就可以跑起來(lái)了。

當(dāng)然這里還有很多的不足之處,我們后續(xù)都會(huì)一一優(yōu)化。

完整代碼地址

為了便于學(xué)習(xí),完整版本代碼以開(kāi)源:

https://github.com/houbb/mybatis

http://weixin.qq.com/r/GSnk-PfEiar2rbOf93wL<br> (二維碼自動(dòng)識(shí)別)

總結(jié)

以上是生活随笔為你收集整理的mybatis 无法初始化类_从零开始手写 mybatis(一)MVP 版本的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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