本文首先講解從JDBC到mybatis的演變過程,然后是使用mybatis進行開發的兩種方式。
一 JDBC的使用及其優化
1.使用JDBC進行數據庫操作
-
加載JDBC驅動;
-
建立並獲取數據庫連接;
-
創建 JDBC Statements 對象;
-
設置SQL語句的傳入參數;
-
執行SQL語句並獲得查詢結果;
-
對查詢結果進行轉換處理並將處理結果返回;
-
釋放相關資源(關閉Connection,關閉Statement,關閉ResultSet);
public static List<Map<String,Object>> queryForList(){ Connection connection = null; ResultSet rs = null; PreparedStatement stmt = null; List<Map<String,Object>> resultList = new ArrayList<Map<String,Object>>(); try { // 加載JDBC驅動 Class.forName("com.mysql.jdbc.Driver").newInstance(); String url = "jdbc:mysql://localhost:3306/silk"; String user = "root"; String password = "123456"; // 獲取數據庫連接 connection = (Connection) DriverManager.getConnection(url,user,password); String sql = "select * from goods where id = ? "; // 創建Statement對象(每一個Statement為一次數據庫執行請求) stmt = (PreparedStatement) connection.prepareStatement(sql); // 設置傳入參數 stmt.setString(1, "1"); // 執行SQL語句 rs = stmt.executeQuery(); // 處理查詢結果(將查詢結果轉換成List<Map>格式) ResultSetMetaData rsmd = rs.getMetaData(); int num = rsmd.getColumnCount(); while(rs.next()){ Map map = new HashMap(); for(int i = 0;i < num;i++){ String columnName = rsmd.getColumnName(i+1); map.put(columnName,rs.getString(columnName)); } resultList.add(map); } } catch (Exception e) { e.printStackTrace(); } finally { try { // 關閉結果集 if (rs != null) { rs.close(); rs = null; } // 關閉執行 if (stmt != null) { stmt.close(); stmt = null; } if (connection != null) { connection.close(); connection = null; } } catch (SQLException e) { e.printStackTrace(); } } return resultList; }
2.JDBC操作數據庫有哪些問題,如何進行優化呢?
2.1 問題:每一次請求都要進行數據庫的連接和關閉,過於頻繁,浪費資源,降低了系統的性能。
解決:數據庫的連接和關閉可以通過數據庫連接池來解決,通過連接池可以反復的使用已經建立的連接去訪問數據庫,而不是每次都重新建立一個新的連接。
2.2 問題:連接池有很多種,如c3p0,dbcp,druid 可能存在變化
解決:可以通過DataSource進行隔離解耦,統一從DataSource獲取數據庫連接,用戶可以通過DataSource來配置使用哪種連接池
2.3 問題:使用JDBC時,sql語句是散落在各個java文件中的,可讀性差不利於維護,改動sql時需要重新打包編譯部署,不利於取出sql在客戶端執行。
解決:將sql統一放到配置文件中,那么就需要將sql提前加載
2.4 問題:參數傳遞是按順序,根據占位符一一匹配的,那如果是多個不確定的參數,這種方式就顯得很局限了。解決:mybatis可以根據參數的不同,生成動態的sql語句
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" default-autowire="byName"> // 此處配置數據源 省略。。。
<!-- 將數據源映射到sqlSessionFactory中 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="classpath:mybatis/mybatis-config.xml" /> <property name="dataSource" ref="dataSource" /> </bean> <!-- SqlSession模板類實例 --> <bean id="sessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="close"> <constructor-arg index="0" ref="sqlSessionFactory" /> </bean> </beans>
3.2 編寫dao接口和它的實現類,將sqlSession注入到實現類中
public interface BaseDao<T> { /** * 根據條件獲取一個元素 * @param paramMap * @return */ T get(T entity); /** * 插入記錄 * @param entity */ int insert(T entity); /** * 插入記錄(批量) * @param list * @return */ int insert(List<T> list); /** * 更新記錄 * @param entity * @return */ int update(T entity); /** * 更新記錄(批量) * @param list * @return */ int update(List<T> list); /** * 刪除記錄 * @param obj * @return */ int delete(T entity); }
3.3 BaseDaoImpl實現類,並將sqlSession注入到此實現類中,sqlSession中封裝了對數據庫的各種操作:
public abstract class BaseDaoImpl<T> extends SqlSessionDaoSupport implements BaseDao<T> { public static final String SQL_SELECT = "select"; public static final String SQL_SELECT_BY = "selectBy"; public static final String SQL_SELECT_PAGE = "selectPage"; public static final String SQL_INSERT = "insert"; public static final String SQL_DELETE = "delete"; public static final String SQL_UPDATE = "update"; public static final String SQL_BATCH_INSERT = "batchInsert"; @Autowired protected SqlSessionTemplate sessionTemplate; @Autowired protected SqlSessionFactory sessionFactory;
@Override @SuppressWarnings("unchecked") public T get(T entity) { if (entity == null) { return null; } Object result = this.getSqlSession().selectOne(this.getSqlName(SQL_SELECT), entity); if (result == null) { return null; } return (T) result; } @Override public int insert(T entity) { if (entity == null) { throw new RuntimeException("T is null"); } int result = this.sessionTemplate.insert(this.getSqlName(SQL_INSERT), entity); return result; } @Override public int insert(List<T> list) { if (list == null || list.size() <= 0) { return 0; } this.sessionTemplate.insert(this.getSqlName(SQL_BATCH_INSERT), list); return list.size(); } @Override public int update(T entity) { if (entity == null) { throw new RuntimeException(""); } int result = this.sessionTemplate.update(this.getSqlName(SQL_UPDATE), entity); return result; }
protected String getSqlName(String sqlId) { StringBuilder sb = new StringBuilder(); sb.append(this.getClass().getName()); sb.append("."); sb.append(sqlId); return sb.toString(); } }
3.4 再編寫對應的Mapper文件,此處省略。
4.Mapper代理開發模式:不需要編寫實現類,只要遵守以下規范,mybatis就可以動態生成Mapper接口的代理類
Mapper代理開發模式需要遵守以下規范:
4.1 Mapper.xml文件中的namespace與mapper接口的類路徑相同。
4.2 Mapper接口方法名和Mapper.xml中定義的每個statement的id相同
4.3 Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql 的parameterType的類型相同
4.4 Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同
這種開發模式,一般通過sqlSession.getMapper(XXXMapper.class),根據Mapper接口聲明的方法和Mapper映射文件來生成一個Mapper代理對象,當通過這個代理對象調用一個Mapper接口聲明的方法時,會根據方法名和參數來找到Mapper映射文件中
的statementId,底層還是通過sqlSeesion.select(statementId,params)來和數據庫進行交互的。
優缺點比較:Mapper代理開發模式不需要編寫實現類,減少了代碼量,同時是面向接口編程,推薦使用此種方式。