
2011年9月22日
> 引言
在Jorm中,主鍵的生成策略主要有AUTO、UUID、GUID、FOREIGN、SEQUENCE、INCREMENT、IDENTITY、ASSIGNED,下面分別來講述這幾種策略的應(yīng)用場(chǎng)景
> GenerationType.AUTO
Jorm的默認(rèn)主鍵策略,自動(dòng)增長(zhǎng)型,自增步長(zhǎng)為1,適用數(shù)據(jù)類型int,long,如:
private int id // 默認(rèn)策略就是AUTO,故可以不寫主鍵策略
或
@Id(GenerationType.AUTO) // 默認(rèn)策略可以省去不寫的哦~
private int id
> GenerationType.INCREMENT
顧名思義,增長(zhǎng)型,適用數(shù)據(jù)類型int,long。自增步長(zhǎng)為1
1> 使用默認(rèn)自增步長(zhǎng)1,如:
@Id(GenerationType.INCREMENT)
@Column("item_id")
private long id;
2> 使用自定義步長(zhǎng),如:
@Id(value = GenerationType.INCREMENT, incrementBy=3) // 這里自增步長(zhǎng)為3,注意寫法
private int id;
> GenerationType.IDENTITY
對(duì)于那些實(shí)現(xiàn)了自動(dòng)增長(zhǎng)的數(shù)據(jù)庫(kù),可以使用IDENTITY,如MySQL,SQL Server,PostreSQL,前提是
MySQL數(shù)據(jù)庫(kù)中建表語(yǔ)句定義了主鍵為:id(你的主鍵列名) int NOT NULL AUTO_INCREMENT 或
id(你的主鍵列名) bigint NOT NULL AUTO_INCREMENT
SQL Server數(shù)據(jù)庫(kù)中建表語(yǔ)句定義了主鍵為:id int identity(xx, xx) 如此類似
PostreSQL數(shù)據(jù)庫(kù)中建表語(yǔ)句定義了主鍵為:id bigserial 或 id serial
使用例子
@Id(GenerationType.IDENTITY)
@Column("id")
private long sid;
> GenerationType.UUID
與數(shù)據(jù)庫(kù)無關(guān)的策略,適用數(shù)據(jù)類型:字符串類型,適用所有數(shù)據(jù)庫(kù),長(zhǎng)度須大于或等于32
@Id(GenerationType.UUID)
private String id;
> GenerationType.GUID
與UUID有點(diǎn)類似,不過這個(gè)id值是又?jǐn)?shù)據(jù)庫(kù)來生成的,適用于數(shù)據(jù)庫(kù)MySQL、PostgreSQL、SQL Server、Oracle等
@Id(GenerationType.GUID)
private String id;
> GenerationType.FOREIGN
適用于一對(duì)一關(guān)系中引用了另一個(gè)對(duì)象的主鍵作為自己的主鍵的情形,如:
@Id(GenerationType.FOREIGN)
@Column("identity_number")
private String identity;
> GenerationType.SEQUENCE
這個(gè)不用多說,應(yīng)用于Oracle、H2、PostgreSQL等有sequence序列功能的數(shù)據(jù)庫(kù)
> GenerationType.ASSIGNED
用戶自定義生成,需要由程序員手工給主鍵主動(dòng)賦值
posted @
2011-10-10 15:17 jadmin 閱讀(1491) |
評(píng)論 (3) |
編輯 收藏
直接上代碼吧:
> Demo one
public void batch_op_one() {
session = Jorm.getSession();
JdbcBatcher batcher = session.createBatcher();
batcher.addBatch("delete from t_id_auto");
batcher.addBatch("delete from t_incre");
batcher.addBatch("delete from t_user");
batcher.execute();
session.beginTransaction();
long start;
try {
start = System.currentTimeMillis();
String sql = "INSERT INTO t_user(sex,age,career,name,id) VALUES(?,?,?,?,?)";
for (int i = 0; i < 100000; i++) {
batcher.addBatch(sql, new Object[] {"男", Numbers.random(98), Strings.random(10), Strings.fixed(6), (i+1) });}
String sqlx = "INSERT INTO t_id_auto(name, id) VALUES(?, ?)";
for (int i = 0; i < 100000; i++) {
batcher.addBatch(sqlx, new Object[] {Strings.fixed(6), (i+1)});
if(i > 200) {
//Integer.parseInt("kkk");
}
}
batcher.execute();
System.out.println(System.currentTimeMillis() - start);
} catch (Exception e) {
session.rollback();
} finally {
session.endTransaction();
session.close();
}
}
> Demo two
public void batch_op_two() {
session = Jorm.getSession();
session.beginTransaction();
session.clean(User.class);
JdbcBatcher batcher = session.createBatcher();
batcher.setBatchSize(500);// 指定每批處理的記錄數(shù)
User u;
int times = 20 * 100;
long start = System.currentTimeMillis();
for(int i = 0; i < times; i++) {
String sex = (i % 2 == 0 ? "男" : "女");
u = new User(Strings.fixed(6), sex, Numbers.random(100), Strings.random(16));
batcher.save(u);
}
batcher.execute();
session.endTransaction();
long cost = (System.currentTimeMillis() - start);
System.out.println("Total:" + cost);
System.out.println("Each:" + (float) cost / times);
session.close();
}
項(xiàng)目地址:http://javaclub.sourceforge.net/jorm.html
下載地址: http://sourceforge.net/projects/javaclub/files/jorm/
posted @
2011-10-09 20:09 jadmin 閱讀(1293) |
評(píng)論 (0) |
編輯 收藏
關(guān)系數(shù)據(jù)庫(kù)不支持繼承,我們可以做如下的映射,這些映射都是犧牲關(guān)系模式的范式基礎(chǔ)的
?
1, ?用一個(gè)表包含所有繼承層次的所有字段,然后標(biāo)識(shí)列來標(biāo)示是哪個(gè)類。這種映射方法最簡(jiǎn)單,但是是違反規(guī)范化的,而且有些字段要強(qiáng)制為NULL值,無法保證關(guān)系數(shù)據(jù)模型的數(shù)據(jù)完整性,這種映射方式性能最高,最簡(jiǎn)單。
?
2, ?每個(gè)具體類一張表(意思就是父類不需要表),所有父屬性在具體類表中重復(fù),這種映射如果要查詢父類要全部掃描子類表,而且一旦父類變化,這些字表要全部變化。
?
3, ?每個(gè)類一張表,表里只包含所屬類的屬性,然后子類和父類共享外鍵,這種映射避免了第2種的可怕的修改,但是查詢的時(shí)候要執(zhí)行連接。
??????在一般情況下,在新增領(lǐng)域?qū)ο蠛螅夹枰@取對(duì)應(yīng)的主鍵值。使用應(yīng)用層來維護(hù)主鍵,在一定程度上有利于程序性能的優(yōu)化和應(yīng)用移植性的提高。在采用數(shù)據(jù)庫(kù)自增主鍵的方案里,如果JDBC驅(qū)動(dòng)不能綁定新增記錄對(duì)應(yīng)的主鍵,就需要手工執(zhí)行查詢語(yǔ)句以獲取對(duì)應(yīng)的主鍵值,對(duì)于高并發(fā)的系統(tǒng),這很容易返回錯(cuò)誤的主鍵。通過帶緩存的DataFieldMaxValueIncrementer,可以一次獲取批量的主鍵值,供多次插入領(lǐng)域?qū)ο髸r(shí)使用,它的執(zhí)行性能是很高的。
??????我們經(jīng)常使用數(shù)據(jù)的自增字段作為表主鍵,也即主鍵值不在應(yīng)用層產(chǎn)生,而是在新增記錄時(shí),由數(shù)據(jù)庫(kù)產(chǎn)生。這樣,應(yīng)用層在保存對(duì)象前并不知道對(duì)象主鍵值,而必須在保存數(shù)據(jù)后才能從數(shù)據(jù)庫(kù)中返回主鍵值。在很多情況下,我們需要獲取新對(duì)象持久化后的主鍵值。在Hibernate等ORM框架,新對(duì)象持久化后,Hibernate會(huì)自動(dòng)將主鍵值綁定到對(duì)象上,給程序的開發(fā)帶來了很多方便。?
??????在JDBC 3.0規(guī)范中,當(dāng)新增記錄時(shí),允許將數(shù)據(jù)庫(kù)自動(dòng)產(chǎn)生的主鍵值綁定到Statement或PreparedStatement中。
??????使用Statement時(shí),可以通過以下方法綁定主鍵值:?int executeUpdate(String sql, int autoGeneratedKeys)?
??????也可以通過Connection創(chuàng)建綁定自增值的PreparedStatement:?PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)?
??????當(dāng)autoGeneratedKeys參數(shù)設(shè)置為Statement.RETURN_GENERATED_KEYS值時(shí)即可綁定數(shù)據(jù)庫(kù)產(chǎn)生的主鍵值,設(shè)置為Statement.NO_GENERATED_KEYS時(shí),不綁定主鍵值。下面的代碼演示了Statement綁定并獲取數(shù)據(jù)庫(kù)產(chǎn)生的主鍵值的過程:
????? Statement stmt = conn.createStatement();
????? String sql = "INSERT INTO t_topic(topic_title,user_id) VALUES(‘測(cè)試主題’,’123’)";
????? stmt.executeUpdate(sql,Statement.RETURN_GENERATED_KEYS); // ①指定綁定表自增主鍵值
????? ResultSet rs = stmt.getGeneratedKeys();
????? if( rs.next() ) {
?????????? intkey = rs.getInt(); // ②獲取對(duì)應(yīng)的表自增主鍵值
????? }
??????Spring利用這一技術(shù),提供了一個(gè)可以返回新增記錄對(duì)應(yīng)主鍵值的方法:?int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder)?,其中第二個(gè)參數(shù)類型org.springframework.jdbc.support.KeyHolder,它是一個(gè)回調(diào)接口,Spring使用它保存新增記錄對(duì)應(yīng)的主鍵,該接口的接口方法描述如下:?
??????Number getKey() throws InvalidDataAccessApiUsageException;
??????當(dāng)僅插入一行數(shù)據(jù),主鍵不是復(fù)合鍵且是數(shù)字類型時(shí),通過該方法可以直接返回新的主鍵值。如果是復(fù)合主鍵,或者有多個(gè)主鍵返回時(shí),該方法拋出 InvalidDataAccessApiUsageException。該方法是最常用的方法,因?yàn)橐话闱闆r下,我們一次僅插入一條數(shù)據(jù)并且主鍵字段類型為數(shù)字類型;?
??????如果是復(fù)合主鍵,則列名和列值構(gòu)成Map中的一個(gè)Entry。如果返回的是多個(gè)主鍵,則拋出InvalidDataAccessApiUsageException異常;?
??????Map getKeys() throws InvalidDataAccessApiUsageException;
??????如果返回多個(gè)主鍵,即PreparedStatement新增了多條記錄,則每一個(gè)主鍵對(duì)應(yīng)一個(gè)Map,多個(gè)Map構(gòu)成一個(gè)List。
??????List getKeyList():?
??????Spring為KeyHolder接口指代了一個(gè)通用的實(shí)現(xiàn)類GeneratedKeyHolder,該類返回新增記錄時(shí)的自增長(zhǎng)主鍵值。假設(shè)我們希望在新增論壇板塊對(duì)象后,希望將主鍵值加載到對(duì)象中,則可以按以下代碼進(jìn)行調(diào)整:
????? public voidaddForum(final Forum forum) {
??????????? final String sql = "INSERT INTO t_forum(forum_name,forum_desc) VALUES(?,?)";
??????????? KeyHolder keyHolder = newGeneratedKeyHolder(); // ①創(chuàng)建一個(gè)主鍵執(zhí)有者
??????????? getJdbcTemplate().update(newPreparedStatementCreator() {
????????????????? public PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
??????????????????????? PreparedStatement ps = conn.prepareStatement(sql);
??????????????????????? ps.setString(1, forum.getForumName());
??????????????????????? ps.setString(2, forum.getForumDesc());
??????????????????????? returnps;
????????????????? }
??????????? }, keyHolder);
??????????? forum.setForumId(keyHolder.getKey().intValue()); // ②從主鍵執(zhí)有者中獲取主鍵
????? }
??????這樣,在調(diào)用addForum(Forum forum)新增forum領(lǐng)域?qū)ο蠛螅琭orum將擁有對(duì)應(yīng)的主鍵值,方便后繼的使用。在JDBC 3.0之前的版本中,PreparedStatement不能綁定主鍵,如果采用表自增鍵(如MySQL的auto increment或SQLServer的identity)將給獲取正確的主鍵值帶來挑戰(zhàn)——因?yàn)槟惚仨氃诓迦霐?shù)據(jù)后,馬上執(zhí)行另一條獲取新增主鍵的查詢語(yǔ)句。下面給出了不同數(shù)據(jù)庫(kù)獲取最新自增主鍵值的查詢語(yǔ)句:?
1) Assigned
主鍵由外部程序負(fù)責(zé)生成,無需Hibernate參與。
2) hilo
通過hi/lo 算法實(shí)現(xiàn)的主鍵生成機(jī)制,需要額外的數(shù)據(jù)庫(kù)表保存主鍵生成歷史狀態(tài)。
3) seqhilo
與hilo 類似,通過hi/lo 算法實(shí)現(xiàn)的主鍵生成機(jī)制,只是主鍵歷史狀態(tài)保存在Sequence中,適用于支持Sequence的數(shù)據(jù)庫(kù),如Oracle。
4) increment
主鍵按數(shù)值順序遞增。此方式的實(shí)現(xiàn)機(jī)制為在當(dāng)前應(yīng)用實(shí)例中維持一個(gè)變量,以保存著當(dāng)前的最大值,之后每次需要生成主鍵的時(shí)候?qū)⒋酥导?作為主鍵。 這種方式可能產(chǎn)生的問題是:如果當(dāng)前有多個(gè)實(shí)例訪問同一個(gè)數(shù)據(jù)庫(kù),那么由于各個(gè)實(shí)例各自維護(hù)主鍵狀態(tài),不同實(shí)例可能生成同樣的主鍵,從而造成主鍵重復(fù)異常。因此,如果同一數(shù)據(jù)庫(kù)有多個(gè)實(shí)例訪問,此方式必須避免使用。
5) identity
采用數(shù)據(jù)庫(kù)提供的主鍵生成機(jī)制。如DB2、SQL Server、MySQL中的主鍵生成機(jī)制。
6) sequence
采用數(shù)據(jù)庫(kù)提供的sequence 機(jī)制生成主鍵。如Oralce 中的Sequence。
7) native
由Hibernate根據(jù)底層數(shù)據(jù)庫(kù)自行判斷采用identity、hilo、sequence其中一種作為主鍵生成方式。
8) uuid.hex
由Hibernate基于128 位唯一值產(chǎn)生算法生成16 進(jìn)制數(shù)值(編碼后以長(zhǎng)度32 的字符串表示)作為主鍵。
9) uuid.string
與uuid.hex 類似,只是生成的主鍵未進(jìn)行編碼(長(zhǎng)度16)。在某些數(shù)據(jù)庫(kù)中可能出現(xiàn)問題(如PostgreSQL)。
10) foreign
使用外部表的字段作為主鍵。一般而言,利用uuid.hex方式生成主鍵將提供最好的性能和數(shù)據(jù)庫(kù)平臺(tái)適應(yīng)性。
另外由于常用的數(shù)據(jù)庫(kù),如Oracle、DB2、SQLServer、MySql 等,都提供了易用的主鍵生成機(jī)制(Auto-Increase 字段或者Sequence)。我們可以在數(shù)據(jù)庫(kù)提供的主鍵生成機(jī)制上,采用generator-class=native的主鍵生成方式。不過值得注意的是,一些數(shù)據(jù)庫(kù)提供的主鍵生成機(jī)制在效率上未必最佳,
大量并發(fā)insert數(shù)據(jù)時(shí)可能會(huì)引起表之間的互鎖。數(shù)據(jù)庫(kù)提供的主鍵生成機(jī)制,往往是通過在一個(gè)內(nèi)部表中保存當(dāng)前主鍵狀態(tài)(如對(duì)于自增型主鍵而言,此內(nèi)部表中就維護(hù)著當(dāng)前的最大值和遞增量), 之后每次插入數(shù)據(jù)會(huì)讀取這個(gè)最大值,然后加上遞增量作為新記錄的主鍵,之后再把這個(gè)新的最大值更新回內(nèi)部表中,這樣,一次Insert操作可能導(dǎo)致數(shù)據(jù)庫(kù)內(nèi)部多次表讀寫操作,同時(shí)伴隨的還有數(shù)據(jù)的加鎖解鎖操作,這對(duì)性能產(chǎn)生了較大影響。 因此,對(duì)于并發(fā)Insert要求較高的系統(tǒng),推薦采用uuid.hex 作為主鍵生成機(jī)制。
如果需要采用定制的主鍵生成算法,則在此處配置主鍵生成器,主鍵生成器須實(shí)現(xiàn)org.hibernate.id.IdentifierGenerator 接口
?
關(guān)鍵詞: Hibernate? 主鍵?? 主鍵生成方式? IdentifierGenerator
?
posted @
2011-09-25 13:47 jadmin 閱讀(1006) |
評(píng)論 (0) |
編輯 收藏
摘要:
閱讀全文
posted @
2011-09-23 16:17 jadmin 閱讀(1272) |
評(píng)論 (1) |
編輯 收藏
主要更新:
----------------------------------------
* [35] fix: oracle下一個(gè)分頁(yè)取limit數(shù)錯(cuò)誤的bug.
* [34] fix: oracle下檢測(cè)是否支持Savepoints時(shí),一個(gè)未捕獲的異常.
* [33] add: 對(duì)bonecp的支持
* [32] add: 對(duì)proxool的支持
* [31] add: 對(duì)commons-dbcp的支持
* [30] fix: classpath沒有config.properties文件會(huì)報(bào)錯(cuò)
> 引言
有時(shí)候我們有這樣的需求,對(duì)象有一個(gè)屬性可能有多個(gè)值,需要在數(shù)據(jù)庫(kù)中作為一個(gè)字段存儲(chǔ)
還是以User為例,career存儲(chǔ)多個(gè)職業(yè)
> 建表
以MySQL為例,執(zhí)行下面的sql建立數(shù)據(jù)表
CREATE TABLE `t_user` (
`id` int(11) NOT NULL,
`name` varchar(50) DEFAULT NULL,
`sex` char(4) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`career` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
> 代碼
實(shí)體類 User.java
@Entity(table = "t_user")
@PK(value = "id")
public class User implements Serializable {
/** desc */
private static final long serialVersionUID = -4750351638245912867L;
@Id
private int id;
private String name;
private String sex;
private Integer age;
@Basic(processor=DefinedFieldProcessor.class)
private String[] career;
@NoColumn
private int kvalue;
public JawaUser() {
super();
}
public JawaUser(String name, String sex, Integer age, String[] career) {
super();
this.name = name;
this.sex = sex;
this.age = age;
this.career = career;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String[] getCareer() {
return career;
}
public void setCareer(String[] career) {
this.career = career;
}
public int getKvalue() {
return kvalue;
}
public void setKvalue(int kvalue) {
this.kvalue = kvalue;
}
public String toString() {
return "User [age=" + age + ", career=" + Arrays.toString(career)
+ ", id=" + id + ", kvalue=" + kvalue + ", name=" + name
+ ", sex=" + sex + "]";
}
}
屬性字段處理類 DefinedFieldProcessor.java
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.javaclub.jorm.Session;
import org.javaclub.jorm.common.CommonUtil;
import org.javaclub.jorm.common.Reflections;
import org.javaclub.jorm.jdbc.process.FieldProcessor;
public class DefinedFieldProcessor implements FieldProcessor {
public Object insert(Session session, Object entity, Field field) {
String[] crs = (String[]) Reflections.getFieldValue(entity, field);
if(!CommonUtil.isEmpty(crs)) {
StringBuilder sbf = new StringBuilder();
for (int i = 0; i < crs.length; i++) {
if(i > 0) {
sbf.append(",");
}
sbf.append(crs[i]);
}
return sbf.toString();
}
return "";
}
public void load(Session session, Object entity, Field field, ResultSet rs,
int idx) throws SQLException {
String str = rs.getString(idx);
String[] crs = str.split(",");
Reflections.setFieldValue(entity, field, crs);
}
}> 測(cè)試
import org.javaclub.jorm.Jorm;
import org.javaclub.jorm.Session;
import org.javaclub.jorm.common.Numbers;
import org.javaclub.jorm.common.Strings;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class FieldProcessorTest {
static Session session;
@BeforeClass
public static void setUpBeforeClass() {
session = Jorm.getSession();
}
@AfterClass
public static void destroy() {
Jorm.free();
}
@Test
public void test_save() {
session.clean(User.class);
User u;
for (int i = 0; i < 100; i++) {
String sex = (i % 2 == 0 ? "男" : "女");
String[] cr = {};
if(i % 3 == 0) {
cr = new String[] {Strings.fixed(2), Strings.random(5), Strings.fixed(6)};
} else if(i % 3 == 1) {
cr = new String[] {Strings.fixed(2), Strings.random(5)};
} else {
cr = new String[] {Strings.fixed(2)};
}
u = new User(Strings.fixed(6), sex, Numbers.random(100), cr);
session.save(u);
}
for (int i = 0; i < 10; i++) {
u = session.read(User.class, i + 1);
System.out.println(u);
}
}
}
posted @
2011-09-22 20:16 jadmin 閱讀(1217) |
評(píng)論 (0) |
編輯 收藏