Oracle數據庫到MySQL數據庫遷移過程中的一大難題就是主鍵生成策略的替換. 如果之前的程序中使用Oracle的Sequence
機制來實現主鍵的自增的話. MySQL中需要使用TableGenerator
進行等價替換.
替換的時候, 主要有三個地方需要修改:
- 以註解方式完成hibernate映射的實體;
- 以xml方式完成hibernate映射的實體;
- 數據庫存儲過程;
註解方式完成hibernate映射的實體的修改
使用Oracle Sequence
假如你之前的程序使用的是Sequence
, 這裏以一個名爲SEQ
的Sequence
爲例, 那麼你操作id
字段的代碼應該長的是下面這個樣子:
// 注意: 如果不指定sequenceName的話, 默認爲HIBERNATE_SEQUENCE, 這個序列也需要在Oracle中手動建立.
@SequenceGenerator(name = "generator", sequenceName = "SEQ")
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")
public Long getId() {
return id;
}
使用MySQL表模擬Oracle Sequence
首先你需要在MySQL中建立一個表sys_sequence
, 表中有兩個字段, 一個是seq_name
, 代表Oracle序列的名稱, 另一個是current_value
, 代表該序列的當前值(注意: 需要將此初始值設定爲Oracle數據庫中對應序列的當前值.). 表的樣子如下:
> select * from sys_sequence;
+--------------+------------+
| seq_name | curr_value |
+--------------+------------+
| SEQ | 2809 |
+--------------+------------+
. 然後在程序中如下編寫:
/**
* allocationSize是每次程序啓動第一次插入時與之前最大值的差值.
*/
@Id
@TableGenerator(name = "sequence", table = "sys_sequence", pkColumnName = "seq_name", valueColumnName = "curr_value", pkColumnValue = "SEQ", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "sequence")
private Long id;
xml方式完成hibernate映射的實體的修改
使用Oracle Sequence
之前使用Sequence
作爲主鍵生成策略的時候, xml映射文件片段如下:
<id name="id">
<generator class="sequence">
<param name="sequence">SEQ</param>
</generator>
</id>
使用MySQL表模擬Oracle Sequence
表的結構與前面註解方式所用的表結構相同. 這次我們在xml中使用的generator是org.hibernate.id.enhanced.TableGenerator
:
<id name="id">
<generator class="org.hibernate.id.enhanced.TableGenerator">
<param name="table_name">sys_sequence</param>
<param name="segment_column_name">seq_name</param>
<param name="value_column_name">curr_value</param>
<param name="segment_value">SEQ</param>
<param name="increment_size">1</param>
</generator>
</id>
存儲過程的修改
存儲過程中, 主要涉及到Sequence
的nextval()
等方法的替換, 我們同樣可以在MySQL中進行模擬.
使用Oracle Sequence
假設我們現在有一個Sequence
名爲SEQ
, 那麼我們通常在存儲過程中使用如下函數獲得SEQ
的下一個值:
SEQ.nextval
使用MySQL模擬Oracle Sequence
首先需要創建一個表格, 用於存儲所有序列的名稱, 當前值以及遞增步長. 這裏我們繼續沿用前面所述的sys_sequence
表, 不過還需要爲表新增一個字段increment_by
, 我們對照Oracle數據庫的設置手動進行increment_by
初值的設定.
> select * from sys_sequence;
+--------------+------------+--------------+
| seq_name | curr_value | increment_by |
+--------------+------------+--------------+
| SEQ | 2809 | 1 |
+--------------+------------+--------------+
然後在數據庫中新增兩個函數, 一個是currval
, 用於獲取模擬的Sequence
的當前值:
CREATE DEFINER=`root`@`%` FUNCTION `currval`(`v_seq_name` varchar(50)) RETURNS decimal(18,0)
BEGIN
DECLARE v_currval DECIMAL(18);
SET v_currval = 1;
SELECT curr_value INTO v_currval FROM sys_sequence WHERE seq_name = v_seq_name;
RETURN v_currval;
END
另一個是nextval
, 用戶獲取模擬的Sequence
的下一個值:
CREATE DEFINER=`root`@`%` FUNCTION `nextval`(`v_seq_name` varchar(50)) RETURNS decimal(18,0)
BEGIN
UPDATE sys_sequence SET curr_value = curr_value + increment_by WHERE seq_name = v_seq_name;
RETURN currval(v_seq_name);
END
之後在需要使用Sequence
的地方, 使用如下語句替代即可:
nextval('SEQ')
小結
如此一來, 就達到了利用MySQL的表來模擬Oracle的Sequence的目的. 以後每當有需要替換的序列, 都在前面建立的sys_sequence
中新增一行即可.