> GenerationType.AUTO
Jorm的默認主鍵策略,自動增長型,自增步長為1,適用數據類型int,long,如:
private int id // 默認策略就是AUTO,故可以不寫主鍵策略
或
@Id(GenerationType.AUTO) // 默認策略可以省去不寫的哦~
private int id
> GenerationType.INCREMENT
顧名思義,增長型,適用數據類型int,long。自增步長為1
1> 使用默認自增步長1,如:
@Id(GenerationType.INCREMENT)
@Column("item_id")
private long id;
2> 使用自定義步長,如:
@Id(value = GenerationType.INCREMENT, incrementBy=3) // 這里自增步長為3,注意寫法
private int id;
> GenerationType.IDENTITY
對于那些實現了自動增長的數據庫,可以使用IDENTITY,如MySQL,SQL Server,PostreSQL,前提是
MySQL數據庫中建表語句定義了主鍵為:id(你的主鍵列名) int NOT NULL AUTO_INCREMENT 或
id(你的主鍵列名) bigint NOT NULL AUTO_INCREMENT
SQL Server數據庫中建表語句定義了主鍵為:id int identity(xx, xx) 如此類似
PostreSQL數據庫中建表語句定義了主鍵為:id bigserial 或 id serial
使用例子
@Id(GenerationType.IDENTITY)
@Column("id")
private long sid;
> GenerationType.UUID
與數據庫無關的策略,適用數據類型:字符串類型,適用所有數據庫,長度須大于或等于32
@Id(GenerationType.UUID)
private String id;
> GenerationType.GUID
與UUID有點類似,不過這個id值是又數據庫來生成的,適用于數據庫MySQL、PostgreSQL、SQL Server、Oracle等
@Id(GenerationType.GUID)
private String id;
> GenerationType.FOREIGN
適用于一對一關系中引用了另一個對象的主鍵作為自己的主鍵的情形,如:
@Id(GenerationType.FOREIGN)
@Column("identity_number")
private String identity;
> GenerationType.SEQUENCE
這個不用多說,應用于Oracle、H2、PostgreSQL等有sequence序列功能的數據庫
> GenerationType.ASSIGNED
用戶自定義生成,需要由程序員手工給主鍵主動賦值
項目地址:http://javaclub.sourceforge.net/jorm.html
下載地址:http://sourceforge.net/projects/javaclub/files/jorm/
> 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);// 指定每批處理的記錄數
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();
}
項目地址:http://javaclub.sourceforge.net/jorm.html
下載地址: http://sourceforge.net/projects/javaclub/files/jorm/
??????使用Statement時,可以通過以下方法綁定主鍵值:?int executeUpdate(String sql, int autoGeneratedKeys)?
??????也可以通過Connection創建綁定自增值的PreparedStatement:?PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)?
??????當autoGeneratedKeys參數設置為Statement.RETURN_GENERATED_KEYS值時即可綁定數據庫產生的主鍵值,設置為Statement.NO_GENERATED_KEYS時,不綁定主鍵值。下面的代碼演示了Statement綁定并獲取數據庫產生的主鍵值的過程:
????? Statement stmt = conn.createStatement();
????? String sql = "INSERT INTO t_topic(topic_title,user_id) VALUES(‘測試主題’,’123’)";
????? stmt.executeUpdate(sql,Statement.RETURN_GENERATED_KEYS); // ①指定綁定表自增主鍵值
????? ResultSet rs = stmt.getGeneratedKeys();
????? if( rs.next() ) {
?????????? intkey = rs.getInt(); // ②獲取對應的表自增主鍵值
????? }
??????Spring利用這一技術,提供了一個可以返回新增記錄對應主鍵值的方法:?int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder)?,其中第二個參數類型org.springframework.jdbc.support.KeyHolder,它是一個回調接口,Spring使用它保存新增記錄對應的主鍵,該接口的接口方法描述如下:?
??????Number getKey() throws InvalidDataAccessApiUsageException;
??????當僅插入一行數據,主鍵不是復合鍵且是數字類型時,通過該方法可以直接返回新的主鍵值。如果是復合主鍵,或者有多個主鍵返回時,該方法拋出 InvalidDataAccessApiUsageException。該方法是最常用的方法,因為一般情況下,我們一次僅插入一條數據并且主鍵字段類型為數字類型;?
??????如果是復合主鍵,則列名和列值構成Map中的一個Entry。如果返回的是多個主鍵,則拋出InvalidDataAccessApiUsageException異常;?
??????Map getKeys() throws InvalidDataAccessApiUsageException;
??????如果返回多個主鍵,即PreparedStatement新增了多條記錄,則每一個主鍵對應一個Map,多個Map構成一個List。
??????List getKeyList():?
??????Spring為KeyHolder接口指代了一個通用的實現類GeneratedKeyHolder,該類返回新增記錄時的自增長主鍵值。假設我們希望在新增論壇板塊對象后,希望將主鍵值加載到對象中,則可以按以下代碼進行調整:
????? public voidaddForum(final Forum forum) {
??????????? final String sql = "INSERT INTO t_forum(forum_name,forum_desc) VALUES(?,?)";
??????????? KeyHolder keyHolder = newGeneratedKeyHolder(); // ①創建一個主鍵執有者
??????????? 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()); // ②從主鍵執有者中獲取主鍵
????? }
??????這樣,在調用addForum(Forum forum)新增forum領域對象后,forum將擁有對應的主鍵值,方便后繼的使用。在JDBC 3.0之前的版本中,PreparedStatement不能綁定主鍵,如果采用表自增鍵(如MySQL的auto increment或SQLServer的identity)將給獲取正確的主鍵值帶來挑戰——因為你必須在插入數據后,馬上執行另一條獲取新增主鍵的查詢語句。下面給出了不同數據庫獲取最新自增主鍵值的查詢語句:?
2) hilo
通過hi/lo 算法實現的主鍵生成機制,需要額外的數據庫表保存主鍵生成歷史狀態。
3) seqhilo
與hilo 類似,通過hi/lo 算法實現的主鍵生成機制,只是主鍵歷史狀態保存在Sequence中,適用于支持Sequence的數據庫,如Oracle。
4) increment
主鍵按數值順序遞增。此方式的實現機制為在當前應用實例中維持一個變量,以保存著當前的最大值,之后每次需要生成主鍵的時候將此值加1作為主鍵。 這種方式可能產生的問題是:如果當前有多個實例訪問同一個數據庫,那么由于各個實例各自維護主鍵狀態,不同實例可能生成同樣的主鍵,從而造成主鍵重復異常。因此,如果同一數據庫有多個實例訪問,此方式必須避免使用。
5) identity
采用數據庫提供的主鍵生成機制。如DB2、SQL Server、MySQL中的主鍵生成機制。
6) sequence
采用數據庫提供的sequence 機制生成主鍵。如Oralce 中的Sequence。
7) native
由Hibernate根據底層數據庫自行判斷采用identity、hilo、sequence其中一種作為主鍵生成方式。
8) uuid.hex
由Hibernate基于128 位唯一值產生算法生成16 進制數值(編碼后以長度32 的字符串表示)作為主鍵。
9) uuid.string
與uuid.hex 類似,只是生成的主鍵未進行編碼(長度16)。在某些數據庫中可能出現問題(如PostgreSQL)。
10) foreign
使用外部表的字段作為主鍵。一般而言,利用uuid.hex方式生成主鍵將提供最好的性能和數據庫平臺適應性。
另外由于常用的數據庫,如Oracle、DB2、SQLServer、MySql 等,都提供了易用的主鍵生成機制(Auto-Increase 字段或者Sequence)。我們可以在數據庫提供的主鍵生成機制上,采用generator-class=native的主鍵生成方式。不過值得注意的是,一些數據庫提供的主鍵生成機制在效率上未必最佳,
大量并發insert數據時可能會引起表之間的互鎖。數據庫提供的主鍵生成機制,往往是通過在一個內部表中保存當前主鍵狀態(如對于自增型主鍵而言,此內部表中就維護著當前的最大值和遞增量), 之后每次插入數據會讀取這個最大值,然后加上遞增量作為新記錄的主鍵,之后再把這個新的最大值更新回內部表中,這樣,一次Insert操作可能導致數據庫內部多次表讀寫操作,同時伴隨的還有數據的加鎖解鎖操作,這對性能產生了較大影響。 因此,對于并發Insert要求較高的系統,推薦采用uuid.hex 作為主鍵生成機制。
如果需要采用定制的主鍵生成算法,則在此處配置主鍵生成器,主鍵生成器須實現org.hibernate.id.IdentifierGenerator 接口
?
關鍵詞: Hibernate? 主鍵?? 主鍵生成方式? IdentifierGenerator
?
主要更新:
----------------------------------------
* [35] fix: oracle下一個分頁取limit數錯誤的bug.
* [34] fix: oracle下檢測是否支持Savepoints時,一個未捕獲的異常.
* [33] add: 對bonecp的支持
* [32] add: 對proxool的支持
* [31] add: 對commons-dbcp的支持
* [30] fix: classpath沒有config.properties文件會報錯
> 準備
以MySQL為例,執行下面的sql建立數據表
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;
> 引入jar或maven依賴,需要jar包
gerald-jorm-1.0.5.jar 最新版本下載:http://sourceforge.net/projects/javaclub/files
commons-logging-1.1.1.jar
log4j-1.2.14.jar
mysql-connector-java-5.1.6.jar
javassist-3.11.0.GA.jar 或 cglib-nodep-2.2.2.jar (根據實際情況選擇性加入)
> 配置文件
在你的java工程的classpath下建立config.properties和jdbc.cfg.xml文件
config.properties內容:
# 下面路徑可以根據實際情況指定,為相對classpath的路徑地址
jdbc.config.path=jdbc.cfg.xml
jdbc.cfg.xml內容:
<?xml version='1.0' encoding="UTF-8"?>
<jdbc-configuration>
<constant name="show_sql" value="true" />
<constant name="jdbc.batch_size" value="600" />
<constant name="bytecode.provider" value="cglib" />
<connections default="simple">
<connection name="simple">
<property name="connection.implementation">org.javaclub.jorm.jdbc.connection.impl.SimpleConnection</property>
<property name="connection.dialect">MySQLDialect</property>
<property name="connection.driver">com.mysql.jdbc.Driver</property>
<property name="connection.jdbcurl">jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8</property>
<property name="connection.database">test</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>
</connection>
<connection name="c3p0">
<property name="connection.implementation">org.javaclub.jorm.jdbc.connection.impl.PooledConnection</property>
<property name="connection.dialect">MySQLDialect</property>
<property name="connection.driver">com.mysql.jdbc.Driver</property>
<property name="connection.jdbcurl">jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8</property>
<property name="connection.database">test</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="connection.pool.min">1</property>
<property name="connection.pool.max">8</property>
<property name="connection.test.sql">select 1</property>
</connection>
</connections>
</jdbc-configuration>
> 實體類User.java
@PK(value = "id")
@Entity(table="t_user")
public class User {
@Id
private int id;
private String name;
private String sex;
private Integer age;
private String career;
@NoColumn
private int kvalue;
public User() {
super();
}
public User(String name, String sex, Integer age, String career) {
super();
this.name = name;
this.sex = sex;
this.age = age;
this.career = career;
}
public User(Integer id, String name, String sex, Integer age, String career) {
super();
this.id = id;
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() {
StringBuffer sb = new StringBuffer();
sb.append("[" + id + ", " + name + ", " + sex + ", " + age + ", " + career + "]");
return sb.toString();
}
}
這里數據庫字段和java實體類User的屬性在命名上是一致的,如果不一致,比如如果表創建sql為:
CREATE TABLE `t_user` (
`user_id` int(11) NOT NULL,
`user_name` varchar(50) DEFAULT NULL,
`sex` char(4) DEFAULT NULL,
`col_age` int(11) DEFAULT NULL,
`career_job` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
那么對應的實體User應該寫成:
@PK(value = "id")
@Entity(table="t_user")
public class User {
@Id
@Column("user_id")
private int id;
@Column("user_name")
private String name;
// 與數據庫字段命名一致,可以不指定@Column
private String sex;
@Column("col_age")
private Integer age;
@Column("career_job")
private String career;
@NoColumn
private int kvalue;
public User() {
super();
}
public User(String name, String sex, Integer age, String career) {
super();
this.name = name;
this.sex = sex;
this.age = age;
this.career = career;
}
public User(Integer id, String name, String sex, Integer age, String career) {
super();
this.id = id;
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() {
StringBuffer sb = new StringBuffer();
sb.append("[" + id + ", " + name + ", " + sex + ", " + age + ", " + career + "]");
return sb.toString();
}
}
> 對User的增刪查改,UserCrudTest.java,記得引入junit-4.8.2.jar
public class UserCrudTest {
static Session session;
@BeforeClass
public static void before() {
session = Jorm.getSession();
}
@AfterClass
public static void after() {
Jorm.free();
}
@Test
public void save_user() {
session.clean(User.class);
User user = null;
for (int i = 0; i < 600; i++) {
String sex = (i % 2 == 0 ? "男" : "女");
user = new User(Strings.fixed(5), sex, Numbers.random(98), Strings.random(8));
session.save(user);
}
}
@Test // 批量保存
public void batch_save_user() {
session.clean(User.class);
JdbcBatcher batcher = session.createBatcher();
User user = null;
for (int i = 0; i < 600; i++) {
String sex = (i % 2 == 0 ? "男" : "女");
user = new User(Strings.fixed(5), sex, Numbers.random(98), Strings.random(8));
batcher.save(user);
}
batcher.execute();
}
@Test
public void loadUser() {
User user = session.read(User.class, 1);
// 這里user是一個代理對象,因為@Entity(table="t_user", lazy = true)
System.out.println(user.getCareer());// 發出查詢sql
}
@Test
public void deletUser() {
User user = session.read(User.class, 1);
if(null != user) {
session.delete(user);
}
user = session.read(User.class, 1);
System.out.println(user);
}
@Test
public void test_update_proxy() {
User u;
u = session.read(User.class, 2);
Assert.assertNotNull(u);
Assert.assertTrue(u instanceof JormProxy);
u.setName("Gerald.Chen");
session.update(u);
System.out.println(u.getName());
u = session.read(User.class, 2);
Assert.assertTrue("Gerald.Chen".equals(u.getName()));
}
@Test
public void queryUser() {
SqlParams<User> params = new SqlParams<User>();
params.setObjectClass(User.class);
params.setFirstResult(8);
params.setMaxResults(20);
List<User> users = session.list(params);
System.out.println(users.size());
System.out.println(users);
}
}
> 特點
1.支持多數據源管理和配置
2.自動封裝Entity
3.支持事務
4.支持存儲過程的方便調用
5.支持lazy加載
6.支持分頁查詢
7.支持多種數據庫H2,MySQL,Oracle,PostgrSQL,SQLServer
> 要求
1.JDK 1.5 or later
2.如需要lazy加載,需要引入cglib或javaassit,具體可配置
> 示例
1.添加
Session session = Jorm.getSession();
User u = new User("Gerald.Chen", "男", 21, "job");;
session.save(u);
2.刪除
session.clean(User.class);// 清空表
session.delete(User.class, "id > 100");// 指定條件刪除
session.delete(user);
3.查詢
User user = session.read(User.class, 1);// 根據主鍵加載
// 加載第一個
User user = session.loadFirst(User.class, "(SELECT * FROM t_user WHERE id > ?)", 88);
// 分頁查詢
SqlParams<User> params = new SqlParams<User>("SELECT * FROM t_user WHERE id > ?", new Object[] { 6 });
params.setObjectClass(User.class);
params.setFirstResult(3);
params.setMaxResults(10);
List<User> users = session.list(params);
// 查詢單個屬性
String sql = "SELECT name FROM t_user WHERE id = 28";
String name = session.queryUniqueObject(sql);
// 查詢屬性列表
List<String> names = session.list(String.class, "SELECT name FROM t_user WHERE id > ?", 200);
List<Integer> ages = session.list(int.class, "SELECT age FROM t_user WHERE age > 18");
4.存儲過程
final String pro = "{? = call hello_proc(?)}";
String r = session.call(new ProcedureCaller() {
public CallableStatement prepare() throws SQLException {
CallableStatement cs = this.getSession().getConnection().prepareCall(pro);
cs.setString(2, "World");
cs.registerOutParameter(1, Types.CHAR);
return cs;
}
public String callback(CallableStatement cs) throws SQLException {
cs.execute();
return cs.getString(1);
}
});
5.事務
session.clean(User.class);
User u;
session.beginTransaction();
try {
for(int i = 0; i < 1000; i++) {
String sex = (i % 2 == 0 ? "男" : "女");
u = new User(Strings.fixed(6), sex, Numbers.random(100), Strings.random(16));
session.save(u);
if(i == 886) {
Integer.parseInt("kkk");
}
}
session.commit();
} catch (Exception e) {
session.rollback();
} finally {
session.endTransaction();
}
這是一個完全基于JDBC的輕量java orm framework, 目標定位于使用方便,簡單,后續會增加許多新的特性
下載地址:http://sourceforge.net/projects/javaclub/files