資料庫連線分析(1)-從JDBC到MyBatis

方丈發表於2019-04-20

摘要

因為有持久層框架,和Spring的存在,遮蔽了應用層連線資料庫進行操作的細節,導致越來越多的人對資料庫連線這塊不甚瞭解,只知使用方便,不知其原理。所以寫一個資料庫連線的系列文章,總結下本人在資料庫連線方面遇到的問題,和對資料庫連線的理解。

JDBC

jdbc:Java DataBase Connectivity,Java 資料庫連線,一套標準的Java API,用來執行SQL語句。這套命名應該是很老了,畢竟將Data Base直接對映成了關係型資料庫,或者說,像我之前在介紹NoSQL資料庫時多次提到的,NoSQL資料庫還沒有一套統一的訪問標準語句。

jdbc的作用就是將SQL變成了Java API 訪問。

		Connection conn = null;
		Class.forName("com.mysql.jdbc.Driver");
		String url = "jdbc:mysql://localhost:3306/test?"
				+ "user=root&password=123456&useUnicode=true&characterEncoding=UTF8";
		conn = DriverManager.getConnection(url);
		Statement stmt = conn.createStatement();
		String sql = "select *from post where user_id=1";
		ResultSet resultSet = stmt.executeQuery(sql);
		List<Post> posts = new ArrayList<>();
		while (resultSet.next()) {
			Post post = new Post();
			post.setId(resultSet.getLong("id"));
			post.setTitle(resultSet.getString("title"));
			post.setContents(resultSet.getString("contents"));
			posts.add(post);
		}
		stmt.close();
		conn.close();
複製程式碼

其中Connection,DriverManager,Statement這些類都是jdk提供的,不依賴其他的jar。jdk提供了一套通用的SQL訪問API,但是各個資料庫並不相同,有各自的標準,所以各個針對MySQL,Oracle提供了不同的驅動。比如MySQL的驅動。

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.9</version>
		</dependency>
複製程式碼

執行Class.forName("com.mysql.jdbc.Driver");載入MySQL驅動

DriverManager.registerDriver(new Driver());
複製程式碼

這段程式碼中包含了所有的基本的資料庫操作物件 JDBC 四大物件: DataSource , Connection, Statement,ResultSet

  1. 連線url

  2. 連線: 資料庫Server通訊與服務的通訊

  3. statement:把 SQL 語句傳送到 DBMS

  4. ResultSet : 資料庫操作返回結果

後續的其他擴充套件,都是基於以上各個部分的擴充套件

這裡寫圖片描述

獲取connection,構建statement,執行時Java運算元據庫最基本的操作,以後的所有擴充套件都圍繞這個。 從上面的程式碼中我們可以看到這只是一個hello world,在實際開發中,有很多的資料庫操作,如果每個都寫一個,那重複程式碼太多了

Spring-Template--解決重複程式碼

將1的url封裝成了一個DataSource物件

@Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource("jdbc:mysql://localhost:3306/test?"
                + "user=root&password=123456&useUnicode=true&characterEncoding=UTF8");
    }
    @Bean
    public JdbcTemplate jdbcTemplate() {
        return  new JdbcTemplate(dataSource());
    }
複製程式碼

將3 Statement重複程式碼解決了

@Service
public class JdbcTemplateService {
    @Autowired
    private JdbcTemplate mJdbcTemplate;
    
    public void queryById(long userId) {
        String sql = "select * from post where user_id = " + userId;
        List<Post> result =mJdbcTemplate.query(sql, new PostRowMapper());
    }

    class PostRowMapper implements RowMapper<Post> {
        public Post mapRow(ResultSet rs, int rowNum) throws SQLException {
            Post post = new Post();
            post.setId(rs.getLong("id"));
            post.setTitle(rs.getString("title"));
            post.setContents(rs.getString("contents"));
            return post;
        }

    }
}

複製程式碼

解決了連線獲取,查詢重複語句的問題, 但是沒有解決物件對映的問題,需要為每個資料庫物件生成獨自的Mapper

那麼JdbcTemplate是如何做到的呢,因為statemnt的前提是需要一個連線,然後執行。 所以對於Spring框架而言,它當前沒有connection物件,所以它需要一個回撥來執行。

public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
		Assert.notNull(sql, "SQL must not be null");
		Assert.notNull(rse, "ResultSetExtractor must not be null");
		if (logger.isDebugEnabled()) {
			logger.debug("Executing SQL query [" + sql + "]");
		}

		class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
			@Override
			@Nullable
			public T doInStatement(Statement stmt) throws SQLException {
				ResultSet rs = null;
				try {
					rs = stmt.executeQuery(sql);
					return rse.extractData(rs);
				}
				finally {
					JdbcUtils.closeResultSet(rs);
				}
			}
			@Override
			public String getSql() {
				return sql;
			}
		}

		return execute(new QueryStatementCallback());
	}
複製程式碼

這裡寫圖片描述

對外暴露JdbcTemplate, 內部實現是通過回撥方法來實現Statement的執行

連線池

Template只是解決了重複程式碼的問題,並沒有解決連線的資源消耗問題,所以需要連線池,通過一個容器儲存連線,然後複用已有的連線,來實現節約資源的目的

一些開源的資料庫連線池

  • DBCP Apache 下面的,實現了基本的資料庫連線池功能,沒有自動回收空閒連線的功能

  • C3P0 實現了資料來源與JNDI的繫結,目前使用它的開源專案有Hibernate,

  • Druid 阿里的開源資料庫連線池,分庫分表資料庫中介軟體TDDL就是用的這個

資料庫連線池是一個老生常談的話題,不做過多的介紹,在下面的mybatis框架中講它的實現

持久層框架-mybatis

雖然有了JdbcTemplate,但是還缺乏一些操作,比如物件對映,查詢資料的時候希望自動對映到物件上, 另外需要自動生成SQL語句,不需要進行SQL的拼接,賦值。

mybatis的介紹

MyBatis 是一款優秀的持久層框架,它支援定製化 SQL、儲存過程以及高階對映。MyBatis 避免了幾乎所有的 JDBC 程式碼和手動設定引數以及獲取結果集。MyBatis 可以使用簡單的 XML 或註解來配置和對映原生資訊,將介面和 Java 的 POJOs(Plain Old Java Objects,普通的 Java物件)對映成資料庫中的記錄。

重點在於避免了所有的JDBC程式碼,和手動設定引數以及獲取結果集

這裡寫圖片描述
對外暴露:SqlSession 在jdbc中是沒有session這個概念,只有connection.session即會話,mybatis與資料庫的一次互動

內部實現:Executor,StatmentHandler 替換Statement,ResultHander替換resultset,另外多了一個ParameterHandler來處理引數

構建一個SqlSessionFactory

    @Bean
    public SqlSessionFactory sqlSessionFactory() {
        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        Environment environment = new Environment("development", transactionFactory, dataSource());
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session
                .Configuration(environment);
        configuration.addMapper(PostMapper.class);
        return new SqlSessionFactoryBuilder().build(configuration);
    }
複製程式碼

獲取session然後執行

 @Autowired
    private SqlSessionFactory sqlSessionFactory;
    public void queryByUserId(long userId) {
        SqlSession session = sqlSessionFactory.openSession();
        PostMapper postMapper = session.getMapper(PostMapper.class);
        Post post = postMapper.findPostByUserId(userId);
    }
}
複製程式碼

spring-mybatis

spring-mybatis 的作用就是讓你不知道mybatis Spring 將會載入必要的 MyBatis 工廠類和 session 類。由spring來接管資料庫連線的建立。 並且提供一個簡單的方式來注入 MyBatis 資料對映器和 SqlSession 到業務層的 bean 中

相關程式碼

github.com/FS136047217…

mybatis 與hibernate一些區別,下期再講

關注【方丈的寺院】,與方丈一起開始技術修行之路 喜歡這篇文章的話,點個讚唄

在這裡插入圖片描述

參考

www.cnblogs.com/JavaSubin/p…

www.mybatis.org/mybatis-3/z…

www.mybatis.org/spring/zh/i… blog.csdn.net/u014723529/…

github.com/spring-proj…

相關文章