SpringBoot中間件ORM框架實現(xiàn)案例詳解(Mybatis)
源碼地址(已開源):https://gitee.com/sizhaohe/mini-mybatis.git 跟著源碼及下述UML圖來理解上手會更快,拒絕浮躁,沉下心來搞
定義:
ORM:Object Relational Mapping --> 對象關(guān)系映射,是一種程序設(shè)計技術(shù),用于實現(xiàn)面向?qū)ο缶幊陶Z言里面不同類型系統(tǒng)的數(shù)據(jù)之間的轉(zhuǎn)換
需求背景:
記不記得剛開始學(xué)JAVA時,編寫一大串JDBC相關(guān)代碼來進(jìn)行與數(shù)據(jù)庫的交互,日后我們接觸到的MyBatis、MyBatisPlus等都是使用ORM組件來實現(xiàn)的框架。
本篇文章提煉出mybatis【最】經(jīng)典、【最】精簡、【最】核心的代碼設(shè)計,來實現(xiàn)一個【mini-mybatis】,從而熟悉并掌握ORM框架的涉及實現(xiàn)。
方案設(shè)計:

- 中間的四部分處理是ORM框架的核心內(nèi)容
- 這個框架會提供出SqlSession工廠以及調(diào)用方式
代碼展示
UML圖
很重要,建議code前跟我一樣,先將類UML圖整理出來,整個類的依賴關(guān)系及代碼執(zhí)行流程會一目而然。

- 以上為ORM框架實現(xiàn)核心類:加載mysql配置文件、對mapper-xml解析、獲取數(shù)據(jù)庫session、操作數(shù)據(jù)庫及封裝響應(yīng)結(jié)果。
實現(xiàn)細(xì)節(jié)
1.定義sqlsession接口
對數(shù)據(jù)庫的定義和處理,本篇我們只封裝一個 T selectOne(Object param);
public interface SqlSession {
<T> T selectOne(String statement, Object parameter);
void close();
}2.DefaultSqlSession(SqlSession的實現(xiàn))
使用rt.jar包下(java.lang.sql包下)
Connection接口(負(fù)責(zé)與數(shù)據(jù)庫進(jìn)行連接)及PreparedStatement(執(zhí)行具體sql)接口來實現(xiàn)
public class DefaultSqlSession implements SqlSession{
private Connection connection;
private Map<String,XNode> mapperElement;
public DefaultSqlSession(Connection connection, Map<String, XNode> mapperElement) {
this.connection = connection;
this.mapperElement = mapperElement;
}
@Override
public <T> T selectOne(String statement, Object parameter) {
XNode xNode = mapperElement.get(statement);
Map<Integer, String> parameterMap = xNode.getParameter();
try {
PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());
buildParameter(preparedStatement, parameter, parameterMap);
// SQL執(zhí)行結(jié)果集的行數(shù)據(jù)
ResultSet resultSet = preparedStatement.executeQuery();
List<T> objects = resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));
return objects.get(0);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz) {
List<T> list = new ArrayList<>();
try {
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
// 每次遍歷行值
while (resultSet.next()) {
T obj = (T) clazz.newInstance();
for (int i = 1; i <= columnCount; i++) {
Object value = resultSet.getObject(i);
String columnName = metaData.getColumnName(i);
String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);
Method method;
if (value instanceof Timestamp) {
method = clazz.getMethod(setMethod, Date.class);
} else {
method = clazz.getMethod(setMethod, value.getClass());
}
method.invoke(obj, value);
}
list.add(obj);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
@Override
public void close() {
if (null == connection) return;
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
private void buildParameter(PreparedStatement preparedStatement, Object parameter, Map<Integer, String> parameterMap) throws SQLException, IllegalAccessException {
int size = parameterMap.size();
// 單個參數(shù)
if (parameter instanceof Long) {
for (int i = 1; i <= size; i++) {
preparedStatement.setLong(i, Long.parseLong(parameter.toString()));
}
return;
}else{
// TODO 后面緊跟的章節(jié)繼續(xù)補充其他類型的入?yún)?
}
}
}3.定義SqlSessionFactory接口
每次執(zhí)行一個SQL語句,應(yīng)用程序都需要獲取一個SqlSession對象。SqlSession對象是執(zhí)行持久化操作的入口點,可以用于執(zhí)行SQL語句、刷新緩存、提交事務(wù)等操作。建議在使用完SqlSession后,及時關(guān)閉它來釋放資源。
public interface SqlSessionFactory {
SqlSession openSession();
}4.DefaultSqlSessionFactory(上述接口實現(xiàn)類)
構(gòu)造方法中向下傳遞了Configuration配置文件
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
return new DefaultSqlSession(configuration.getConnection(), configuration.getMapperElement());
}
}5.SqlSessionFactoryBuilder
數(shù)據(jù)庫操作的核心類,負(fù)責(zé)解析Mapper文件(拿datasource,數(shù)據(jù)庫連接信息,mapper文件中sql的各個信息如id,入返參類型,sql)
public class SqlSessionFactoryBuilder {
public DefaultSqlSessionFactory build(Reader reader) {
SAXReader saxReader = new SAXReader();
Document document = null;
try {
document = saxReader.read(new InputSource(reader));
// 拿到根標(biāo)簽元素
Element rootElement = document.getRootElement();
Configuration configuration = parseConfiguration(rootElement);
return new DefaultSqlSessionFactory(configuration);
} catch (DocumentException e) {
e.printStackTrace();
}
return null;
}
public Configuration parseConfiguration(Element rootElement) {
Configuration configuration = new Configuration();
configuration.setDataSource(dataSource(rootElement.selectNodes("http://dataSource")));
configuration.setConnection(connection(configuration.getDataSource()));
configuration.setMapperElement(mapperElement(rootElement.selectNodes("http://mappers")));
return configuration;
}
private Map<String, String> dataSource(List<Element> list) {
Map<String, String> dataSource = new HashMap<>(4);
Element element = list.get(0);
List content = element.content();
for (Object o : content) {
Element e = (Element) o;
String name = e.attributeValue("name");
String value = e.attributeValue("value");
dataSource.put(name, value);
}
return dataSource;
}
private Connection connection(Map<String, String> dataSource) {
try {
return DriverManager.getConnection(dataSource.get("url"), dataSource.get("username"), dataSource.get("password"));
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
private Map<String, XNode> mapperElement(List<Element> list) {
Map<String, XNode> map = new HashMap<>();
Element element = list.get(0);
List content = element.content();
try {
for (Object o : content) {
Element e = (Element) o;
// 拿到mapper文件對應(yīng)地址
String resource = e.attributeValue("resource");
Reader reader = Resources.getResourceAsReader(resource);
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new InputSource(reader));
Element rootElement = document.getRootElement();
String namespace = rootElement.attributeValue("namespace");
List<Element> selectNodes = rootElement.selectNodes("select");
for (Element ele : selectNodes) {
String id = ele.attributeValue("id");
String parameterType = ele.attributeValue("parameterType");
String resultType = ele.attributeValue("resultType");
String sql = ele.getText();
// ? 匹配
Map<Integer, String> parameter = new HashMap<>();
Pattern pattern = Pattern.compile("(#\\{(.*?)})");
Matcher matcher = pattern.matcher(sql);
for (int i = 1; matcher.find(); i++) {
String g1 = matcher.group(1);
String g2 = matcher.group(2);
parameter.put(i, g2);
sql = sql.replace(g1, "?");
}
XNode xNode = new XNode();
xNode.setId(id);
xNode.setNameSpace(namespace);
xNode.setParameterType(parameterType);
xNode.setResultType(resultType);
xNode.setSql(sql);
xNode.setParameter(parameter);
map.put(namespace + "." + id, xNode);
}
}
}catch (Exception e){
e.printStackTrace();
}
return map;
}
}測試驗證
建表
DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` bigint(20) NOT NULL COMMENT '自增id', `userId` varchar(9) DEFAULT NULL COMMENT '用戶ID', `userNickName` varchar(32) DEFAULT NULL COMMENT '用戶昵稱', `userHead` varchar(255) DEFAULT NULL COMMENT '用戶頭像', `userPassword` varchar(255) DEFAULT NULL COMMENT '用戶密碼', `createTime` datetime DEFAULT NULL COMMENT '創(chuàng)建時間', `updateTime` datetime NOT NULL COMMENT '更新時間', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of user -- ---------------------------- BEGIN; INSERT INTO `user` VALUES (1, '001', 'xxx', '001', '123', '2023-07-14 17:33:55', '2023-07-14 17:33:58'); INSERT INTO `user` VALUES (2, '002', 'xxx2', '002', '123', '2023-07-14 17:33:55', '2023-07-14 17:33:58'); COMMIT; SET FOREIGN_KEY_CHECKS = 1;
定義POJO及DAO
@Data
public class User {
private Long id;
private String userId; // 用戶ID
private String userNickName; // 昵稱
private String userHead; // 頭像
private String userPassword; // 密碼
private Date createTime; // 創(chuàng)建時間
private Date updateTime; // 更新時間
}public interface IUserDao {
User queryUserInfoById(Long id);
}ORM配置文件--mybatis-config-datasource.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://172.17.1.245:3306/airticketbasedb?useUnicode=true"/>
<property name="username" value="write"/>
<property name="password" value="write123"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/User_Mapper.xml"/>
</mappers>
</configuration>Mapper配置
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.minimybatis.dao.IUserDao">
<select id="queryUserInfoById" parameterType="java.lang.Long" resultType="com.example.minimybatis.po.User">
SELECT id, userId, userNickName, userHead, userPassword, createTime
FROM user
where id = #{id}
</select>
</mapper>測試類
public class ApiTest {
@Test
public void test(){
String resouce = "mybatis-config-datasource.xml";
Reader reader;
try{
reader = Resources.getResourceAsReader(resouce);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne(
"com.example.minimybatis.dao.IUserDao.queryUserInfoById",
1L);
System.out.println(JSONObject.toJSONString(user));
}catch (Exception e){
e.printStackTrace();
}
}
}
總結(jié)
比mybatis小很多,取其(mybaits)精華來達(dá)到掌握ORM框架的目的
到此這篇關(guān)于SpringBoot中間件ORM框架實現(xiàn)案例詳解(Mybatis)的文章就介紹到這了,更多相關(guān)SpringBoot中間件ORM內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java httpclient設(shè)置超時時間和代理的方法
這篇文章主要介紹了java httpclient設(shè)置超時時間和代理的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02

