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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring JDBC-自增键和行集RowSet

發布時間:2025/3/21 javascript 15 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring JDBC-自增键和行集RowSet 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

  • 概述
  • 自增鍵的使用
    • Oracle以序列方式產生主鍵值
    • MySQL以表方式產生主鍵值
  • 如何規劃主鍵方案
  • 自增鍵小結
  • 以行集返回數據
    • 示例
  • 示例源碼

概述

Spring JDBC提供了對自增鍵及行集的支持,自增鍵對象讓用戶可以不依賴數據庫的自增鍵,在應用層為新紀錄提供主鍵。

在Java1.4中引入RowSet,它允許在連接斷開的情況下操作數據。 這里我們討論如何在Spring JDBC中使用RowSet。


自增鍵的使用

一般數據庫都提供了自增鍵的功能,比如MySql的auto_increment , SQL Server的identifty字段等.

Spring允許用戶在應用層產生主鍵值,為此定義了org.springframework.jdbc.supprot.incrementer.DataFieldMaxValueIncrementer接口 , 提供了兩種產生主鍵的方案,第一是通過序列產生主鍵,第二是通過表產生主鍵。 根據主鍵產生方式及數據庫類型的不同,Spring提供了不同的實現類。

DataFieldMaxValueIncrementer繼承類圖

根據不同的主鍵產生方式,可能需要配置表名、主鍵字段或者序列等信息。

DataFieldMaxValueIncrementer接口中定義了3個獲取主鍵值的方法

  • int nextIntValue():獲取下一個主鍵值,主鍵值類型為int

  • long nextLongValue();獲取下一個主鍵值,主鍵值類型為long

  • String nextStringValue();獲取下一個主鍵值,主鍵值類型為String

在其抽象類AbstractDataFieldMaxValueIncrementer中,提供了幾個重要屬性:

incrementerName:定義序列名后模擬序列表的名稱,如果返回的主鍵值類型是String類型,則paddingLength屬性就會派上用場,它允許用戶指定返回主鍵的長度,不足的部分前面補0.

AbstractSequenceMaxAbstractSequence使用標準的數據庫序列產生主鍵值,

而AbstractColumnMaxValueIncrementer使用一張模擬序列的表產生主鍵值,AbstractColumnMaxValueIncrementer可以通過cacheSize屬性指定緩存的主鍵個數,當內存中主鍵值用完后,遞增器將一次性獲取cacheSize個主鍵,這樣可以減少數據庫訪問的次數,提高應用的性能。


下面分別以Oracle和MySQL為例子,分別闡述下使用序列以及字段產生主鍵值的方式。

Oracle以序列方式產生主鍵值

在Oracle數據庫中創建artisan表以及artisan_id的序列

-- Create table create table ARTISAN (artisan_id NUMBER,artisan_name VARCHAR2(50) ) tablespace TAB_CCpctfree 10initrans 1maxtrans 255storage(initial 16next 8minextents 1maxextents unlimited);-- Create sequence create sequence artisan_id_seq minvalue 1 maxvalue 999 start with 1 increment by 1 cache 20;

接著我們調整下,Spring配置文件,使用OracleSequenceMaxValueIncrementer作為主鍵的產生器

<?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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!-- 掃描類包,將標注Spring注解的類自動轉化Bean,同時完成Bean的注入 --><context:component-scan base-package="com.xgj.dao.dataFieldMaxValueIncrementer"/><!-- 使用context命名空間,加載數據庫的properties文件 --><context:property-placeholder location="classpath:spring/jdbc.properties" /><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"p:driverClassName="${jdbc.driverClassName}"p:url="${jdbc.url}"p:username="${jdbc.username}"p:password="${jdbc.password}" /><!-- 配置Jdbc模板 --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"p:dataSource-ref="dataSource" /><!-- 配置主鍵產生器,指定數據源和序列名 --><bean id="oracleIncre" class="org.springframework.jdbc.support.incrementer.OracleSequenceMaxValueIncrementer"p:dataSource-ref="dataSource"p:incrementerName="artisan_id_seq"/></beans>

業務類

package com.xgj.dao.dataFieldMaxValueIncrementer.oracle.dao;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.support.incrementer.OracleSequenceMaxValueIncrementer; import org.springframework.stereotype.Repository;import com.xgj.dao.dataFieldMaxValueIncrementer.oracle.domain.Artisan;/*** * * @ClassName: AritsanOracleDaoImpl* * @Description: @Repository標注DAO層,并被Spring管理* * @author: Mr.Yang* * @date: 2017年9月29日 下午8:39:32*/@Repository public class AritsanOracleDaoImpl implements AritsanOracleDao {private JdbcTemplate jdbcTemplate;private OracleSequenceMaxValueIncrementer oracleIncre;private static final String addArtisanSql = "insert into artisan(artisan_id ,artisan_name) values(?,?)";/*** * * @Title: setOracleIncre* * @Description: 自動注入OracleSequenceMaxValueIncrementer* * @param oracleIncre* * @return: void*/@Autowiredpublic void setOracleIncre(OracleSequenceMaxValueIncrementer oracleIncre) {this.oracleIncre = oracleIncre;}/*** * * @Title: setJdbcTemplate* * @Description: 自動注入JdbcTemplate* * @param jdbcTemplate* * @return: void*/@Autowiredpublic void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}/*** 使用 oracleIncre.nextIntValue() 作為主鍵自增長*/@Overridepublic void addArtisan(Artisan artisan) {jdbcTemplate.update(addArtisanSql, oracleIncre.nextIntValue(),artisan.getArtisanName());System.out.println("add Artisan successfully");}}

單元測試

package com.xgj.dao.dataFieldMaxValueIncrementer.oracle.dao;import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext;import com.xgj.dao.dataFieldMaxValueIncrementer.oracle.domain.Artisan;public class OracleSeqIncreaseTest {ClassPathXmlApplicationContext ctx = null;AritsanOracleDaoImpl aritsanOracleDaoImpl = null;@Beforepublic void initContext() {// 啟動Spring 容器ctx = new ClassPathXmlApplicationContext("classpath:com/xgj/dao/dataFieldMaxValueIncrementer/conf_oracleincreaseId.xml");aritsanOracleDaoImpl = ctx.getBean("aritsanOracleDaoImpl",AritsanOracleDaoImpl.class);System.out.println("initContext successfully");}@Testpublic void queryTeacherById() {for (int i = 0; i < 5; i++) {Artisan artisan = new Artisan();artisan.setArtisanName("Xiao" + i);aritsanOracleDaoImpl.addArtisan(artisan);}}@Afterpublic void closeContext() {if (ctx != null) {ctx.close();}System.out.println("close context successfully");}}

日志

2017-09-29 20:41:28,720 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4a05bc0c: startup date [Fri Sep 29 20:41:28 BOT 2017]; root of context hierarchy 2017-09-29 20:41:28,823 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/dao/dataFieldMaxValueIncrementer/conf_oracleincreaseId.xml] initContext successfully add Artisan successfully add Artisan successfully add Artisan successfully add Artisan successfully add Artisan successfully 2017-09-29 20:41:30,221 INFO [main] (AbstractApplicationContext.java:984) - Closing org.springframework.context.support.ClassPathXmlApplicationContext@4a05bc0c: startup date [Fri Sep 29 20:41:28 BOT 2017]; root of context hierarchy close context successfully

測試結果

觀察ID,是按照定義的序列生成的ID


MySQL以表方式產生主鍵值

在MySQL數據庫中創建一張用于維護artisan主鍵的artisan_id表

create table artisan_id(sequence_id int) type = MYISAM; insert into artisan_id values(0);

由于主鍵維護表的并發訪問量很大,最好將其聲明為MYISAM類型。 此外,需要為該表提供初始值,以便后續主鍵值在此基礎上增長。

Spring配置文件微調

<!-- 配置主鍵產生器,指定數據源和序列名 --><bean id="mysqlIncrease" class="org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer"p:dataSource-ref="dataSource"p:incrementerName="artisan_id"p:columnName="sequence_id"p:cacheSize="10"/>

p:incrementerName –維護主鍵的表名
p:columnName 用于生成主鍵值的列名
p:cacheSize 緩存大小

cacheSize 決定一次返回的主鍵個數,這里設置為10 ,當第一次通過MySQLMaxValueIncrementer#nextIntValue()方法獲取主鍵時,MySQLMaxValueIncrementer將使artisan_id.sequence_id 遞增到10 ,而后9次調用nextIntValue方法時,都從緩存中獲取主鍵值,直到第10次調用nextIntValue()方法時,才會再此將artisan_id.sequence_id遞增10 ,如此循環反復.


如何規劃主鍵方案

從主鍵創建者的角度看,我們可以將主鍵創建方案分為兩類:

  • 其一為“應用層主鍵方案”,新數據的主鍵分配由應用層負責,如采用UUID或者使用DataFieldMaxValueIncrementer生成主鍵都屬于這一類型;
  • 其二為“數據庫層主鍵方案”,新數據的主鍵分配由數據庫負責,即在表結構定義時,將主鍵設置為auto
    increment或通過表的觸發器分配主鍵。

1、數據庫層主鍵方案不足:
其一,它給應用開發帶來不便,因為你必須通過一個查詢獲取新增數據的主鍵值;
其二,不方便主鍵值的全局管理和控制,使系統散失靈活性;
其三,不方便數據的整合和遷移。

2、采用應用層主鍵方案,使用UUID產生主鍵值,這樣可以保證ID的全局唯一性,為后期數據整合帶來了便利。
當然,采用UUID也有不好地方,就是UUID是一個36位的字符串,會占用大量的存儲空間。

所以另一個候選的方案就是采用分段長整型編碼方案,將主鍵編碼分為N段:這樣就可以創建一個全局的唯一的整數型的主鍵值。

這里不能使用DataFieldMaxValueIncrementer,因為DataFieldMaxValueIncrementer只能為一個表創建主鍵,但道理是相同,我們可以創建一個包含N個字段的主鍵表,編寫一個類似DataFieldMaxValueIncrementer的接口以獲取主鍵值。


自增鍵小結

在高并發的系統中,如果采用基于序列表的方式創建主鍵值,則應該考慮兩個層面的并發問題:

第一:應用層獲取主鍵的并發問題,Spring的DataFielMaxValueIncrementer實現類已經對獲取主鍵值的代碼進行了同步,確保同一JVM內應用不會產生應發問題

第二:全局的并發問題,如果應用是集群部署的,所有集群節點通過同一個序列表獲取主鍵,那么就必須對這張序列表進行樂觀鎖定(序列表必須添加一個版本或者時間戳字段),以防止集群節點的并發問題。 很可惜的是Spring的DataFielMaxValueIncrementer并滅有對序列表進行樂觀鎖定。我們只有自己實現DataFielMaxValueIncrementer接口,以解決全局并發的問題。

另外 DataFielMaxValueIncrementer接口只能為一張表提供組件,即每張表都必須配置一個單獨的DataFielMaxValueIncrementer,因此比較死板,建議參照DataFielMaxValueIncrementer接口,自行編寫一個可為多張表提供主鍵的接口。


以行集返回數據

行集對象可以綁定一個數據連接并在整個生命周期中維持該連接,在此情況下,該行集對象被稱為“連接的行集”。

行集對象還可以先綁定一個數據源,獲取數據后就關閉它,這種行集被稱為“非連接行集”。 非連接行集可以在斷開連接時更改數據,然后重新綁定數據連接,并將對數據的更改同步到數據庫中。

JdbcTemplate 為獲取基于行集的結果集,提供如下查詢方法

  • SqlRowSet queryForRowSet(String sql)
  • SqlRowSet queryForRowSet(String sql,Object … args)
  • SqlRowSet queryForRowSet(String sql,Object[] args ,int[] argTypes)

示例

package com.xgj.dao.rowset.dao;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.support.rowset.SqlRowSet; import org.springframework.stereotype.Repository;/*** * * @ClassName: AritsanOracleDaoImpl* * @Description: @Repository標注DAO層,并被Spring管理* * @author: Mr.Yang* * @date: 2017年9月29日 下午10:41:10*/@Repository public class AritsanOracleDaoImpl implements AritsanOracleDao {private JdbcTemplate jdbcTemplate;private static final String selectArtisanByIdSql = "select artisan_name from artisan where artisan_id = ?";/*** * * @Title: setJdbcTemplate* * @Description: 自動注入JdbcTemplate* * @param jdbcTemplate* * @return: void*/@Autowiredpublic void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}@Overridepublic SqlRowSet selectArtisanById(int artisanId) {return jdbcTemplate.queryForRowSet(selectArtisanByIdSql, artisanId);}}

單元測試

package com.xgj.dao.rowset.dao;import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.support.rowset.SqlRowSet;public class RowSetTest {ClassPathXmlApplicationContext ctx = null;AritsanOracleDaoImpl aritsanOracleDaoImpl = null;@Beforepublic void initContext() {// 啟動Spring 容器ctx = new ClassPathXmlApplicationContext("classpath:com/xgj/dao/rowset/conf_rowset.xml");aritsanOracleDaoImpl = ctx.getBean("aritsanOracleDaoImpl",AritsanOracleDaoImpl.class);System.out.println("initContext successfully");}@Testpublic void queryTeacherById() {SqlRowSet sqlRowSet = aritsanOracleDaoImpl.selectArtisanById(1);// 這時,數據連接已經斷開while (sqlRowSet.next()) {System.out.println("artisan_name:"+ sqlRowSet.getString("artisan_name"));}}@Afterpublic void closeContext() {if (ctx != null) {ctx.close();}System.out.println("close context successfully");} }

測試結果:

2017-09-29 22:27:14,381 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@70221d9a: startup date [Fri Sep 29 22:27:14 BOT 2017]; root of context hierarchy 2017-09-29 22:27:14,500 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/dao/rowset/conf_rowset.xml] initContext successfully artisan_name:Xiao0 2017-09-29 22:27:16,161 INFO [main] (AbstractApplicationContext.java:984) - Closing org.springframework.context.support.ClassPathXmlApplicationContext@70221d9a: startup date [Fri Sep 29 22:27:14 BOT 2017]; root of context hierarchy close context successfully

在selectArtisanById查詢并返回SqlRowSet的結果集后,數據連接已經斷開,但是結果集的數據已經保存在SqlRowSet中。 因此,我們仍然可以訪問到SqlRowSet的結果集數據。

值的注意的是,RowSet會一次性裝載所有的匹配數據,而不像ResultSet一樣,分批次返回一批數據(一批的行數為fetchSize).

所以對于大結果集的數據,使用SQLRowSet會造成很大的內存消耗,不過JdbcTemplate的maxSize屬性依然會現在SqlRowSet的返回記錄數。


示例源碼

代碼已托管到Github—> https://github.com/yangshangwei/SpringMaster

總結

以上是生活随笔為你收集整理的Spring JDBC-自增键和行集RowSet的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。