项⽬适配oceanBaseoracle数据库mysql适配oracle
最近公司的项⽬需要⽤oceanBase oracle数据库。公司原来⽤的是mysql数据库,中间换的国产的达梦数据库。其中⼀些语法放在
oceanBase oracle ⾥⾯不适⽤,所以需要对程序,数据库做⼀些修改,使项⽬能在oceanBase oracle 上⾯正常运⾏起来。下⾯会挨个说
⼀下遇到的问题,和解决的⽅法。这⾥做⼀下记录。
⽬前发想的问题主要有:
1.表⾥⾯数据库关键字的使⽤,
⽬前发现的有resource 和size. 对于数据库关键字,我们只能去修改代码,把字段信息改了。好在涉及到的地⽅不是很多。
2.数据库字段类型的对应。
对于数据字段类型的匹配可以直接看mysql与oracle数据类型对照表来解决。这⾥给⼀个参考地址:
3.对于数据库特殊函数的兼容。
我这⾥遇到的有CHAR_LENGTH,CONCAT,IFNULL,ISNUMERIC,LEFT,RIGHT,YEAR 具体解决⽅式是在数据库⾥⾯定义同名
函数来代替。具体实现为
CREATE OR REPLACE function CHAR_LENGTH(
parm1 in VARCHAR)
return integer as
v1 int;
begin
select length(parm1) into v1 from dual;
RETURN v1;
end;
-- CONCAT函数 oceanBase 只⽀持两个单数,这⾥定义⼀个多个参数的同名函数,给参数的默认值设置为-- ''. 就能实现其它数据库CONCAT多个参数的功能。不过-- 调⽤⽅式:select SYS.CONCAT('a','b','c') from dual;
CREATE OR REPLACE function CONCAT(
param1 in VARCHAR DEFAULT '',
ba系统架构图param2 in VARCHAR DEFAULT '',
param3 in VARCHAR DEFAULT '',
param4 in VARCHAR DEFAULT '',
param5 in VARCHAR DEFAULT '',
param6 in VARCHAR DEFAULT '',
param7 in VARCHAR DEFAULT '',
param8 in VARCHAR DEFAULT '',
param9 in VARCHAR DEFAULT '',
param10 in VARCHAR DEFAULT '',
param11 in VARCHAR DEFAULT '',
param12 in VARCHAR DEFAULT '',
param13 in VARCHAR DEFAULT '',
param14 in VARCHAR DEFAULT '',
param15 in VARCHAR DEFAULT ''
)
return varchar2 as
v1 int;android bluedroid是什么设备
begin
return param1 || param2 || param3 || param4 || param5 || param6 || param7 || param8 || param9 || param10 || param11 || param12 || param13 || param14 || para end;
CREATE OR REPLACE function IFNULL(
param1 in STRING,
param2 in STRING)
return STRING as
v1 int;
v2 STRING;
begin
select nvl(param1, param2) INTO v2 from dual;
return v2;
end;
CREATE OR REPLACE function ISNUMERIC(
param1 in STRING)
RETURN NUMBER
IS
v_str FLOAT ;
begin
IF param1 IS NULL
THEN
RETURN 0 ;
ELSE
BEGIN
SELECT TO_NUMBER ( param1 )
INTO v_str
FROM DUAL;
EXCEPTION
WHEN INVALID_NUMBER
THEN
RETURN 0 ;
END ;
RETURN 1 ;
END IF ;
end;
-
- 由于LEFT 也是oceanBase 的系统保留关键字,所以调⽤的时候也需要在函数名称前⾯加上⽤户名。
-- select SYS.LEFT('asdfasdf', 3) from dual ;
CREATE OR REPLACE function LEFT(
param in STRING,
num in integer)
return varchar2 as
v1 int;
v2 STRING;
begin
select SUBSTR(param, 1, num) into v2 from dual;
RETURN v2;
end;
CREATE OR REPLACE function RIGHT(
param in STRING,
num in integer)
return varchar2 as
v1 int;
v2 STRING;
begin
select SUBSTR(param, LENGTH(param) -(num -1), num) into v2 from dual;
RETURN v2;
end;
CREATE OR REPLACE function YEAR(
param1 in DATE)
return integer as
v1 int;
begin
select to_number(TO_CHAR(param1, 'yyyy')) into v1 from dual ;
RETURN v1;
end;
其中函数 CONCAT 和函数 LEFT 由于是系统关键字,所以调⽤的时候需要在函数前⾯加上⽤户名才能正常调⽤。调⽤⽅式为
select SYS.CONCAT('a','b','c') from dual;
select SYS.LEFT('asdfasdf', 3) from dual ;
需要在函数前⾯加上⽤户名 SYS. 这样的话在项⽬⾥⾯所有⽤到这两个函数的地⽅都需要单独修改。为了避免⼤批量的修改,给mybatis 加了⼀个。在⾥⾯⾸先判断当前数据库环境,如果是oceanBase 则匹配sql语句中的 CONCAT( 和 LEFT( 替换成
SYS.CONCAT( 和SYS.LEFT( 具体代码如下
@Component
@Intercepts(@Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class,Integer.class }))
public class ObFunctionInterceptor implements Interceptor {
@Autowired
@Lazy
private SqlSessionTemplate sqlSessionTemplate;
@Override
public Object intercept(Invocation invocation) throws Throwable {
String databaseId = Configuration().getDatabaseId();
//判断当前数据库版本是不是oceanBase版本,因为⽤的是oceanBase oracle 我们这⾥直接当做 oracle适配的
if (Code().equals(databaseId) && Target() instanceof StatementHandler) {
StatementHandler statementHandler = (StatementHandler) Target();
BoundSql boundSql = BoundSql();
//获取sql语句
String sql = Sql();
MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
//替换CONCAT(
ains("CONCAT(")){
sql = placeAll("CONCAT\\(", "SYS.CONCAT(");
}
//替换LEFT(
ains("LEFT(")){
sql = placeAll("LEFT\\(", "SYS.LEFT(");
}
//把修改后的sql设置回去。
mysql语句转oraclemetaStatementHandler.setValue("delegate.boundSql.sql", sql);
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
} else {
return target;
}
}
@Override
public void setProperties(Properties properties) {
网页界面设计的功能性主要体现在信息的}
arrow recovery附魔}
4.对于⾃增ID的处理。
oracle数据库不⽀持ID⾃增,想要⾃增只能通过⼀些⽅法来实现。实现主要有两个思路,⼀种是数据库层⾯,⽤序列来实现ID⾃增,另⼀种是代码层⾯,通过查询max值来实现⾃增。两种⽅法各有优劣,⼀个节省资源,对数据库依赖较⾼。 第⼆种有⼀定的资源浪费,不过不依赖数据库,可以⾮常容易的进⾏数据库平移。这⾥我⽤的第⼀种⽅式,第⼆种后⾯有机会说⼀下。补充⼀下⽤代码实现⾃增ID:
数据层⾯⽤序列实现⾃增,就是给表加⼀个触发器。在插⼊的时候获取序列的nextval作为ID。
create or replace TRIGGER TG_XXL_JOB_GROUP BEFORE
INSERT ON "XXL_JOB_GROUP"
FOR EACH ROW
ENABLE
BEGIN
--your trigger body
select XXL_JOB_val into:new.id from dual;
END
5.对于分页的处理。
不同数据库分分页⽅式不⼀样,mysql⽤的是limit, oracle这⾥⽤的是rownum. 在这⾥⽤了⼀个分页插件PageHelper. 它可以根据不同的数据库来⽣成对应的分页语句。具体应⽤为
导⼊PageHelper包
<!--pagehelper-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
<exclusions>
<exclusion>
<groupId&batis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
在配置⽂件设置对应参数
pagehelper:
# 关于pagehelper⽀持哪些数据库⽅⾔参考:com.github.pagehelper.page.PageAutoDialect
helper-dialect: oracle //这⾥最重要,这⾥是区分不同数据库类型来⽣成对应的分页语句的。
# helper-dialect: postgresql
# helper-dialect: mysql
# helper-dialect: oscar
reasonable: true
support-methods-arguments: true
params: count=countSql
配置号之后就可以使⽤了。 使⽤⽅法很简单。
PageInfo<Map<String, Object>> pageInfo =
PageHelper.Page(), Limit())
php类型文件如何打开.doSelectPageInfo(() ->accessRecordMapper.findAccessRecordByPageDTO(pageDTO));
这⾥给⼀个pageHelper的链接:
6.对于VARCHAR字段类型的长度问题。
oracle数据类型⼀个urf8编码的中⽂需要占⽤3个字节。为了避免因为字段长度报错,需要吧所有表⾥⾯VARCHAR类型的数据的长度都扩⼤到3倍。这⾥我们⽤了⼀个数据库建模⼯具PdMan.这⾥给⼀下官⽹ ⽤这个⼯具可以很容易的把字符串的数据类型增加3倍并⽣成DDL脚本。在程序⾥⾯的DDL也是根据数据库类型进⾏处理(长度* 3)。
7.对于CLOB字段类型问题。
oracle⾥⾯CLOB数据类型主要有两个问题⼀个是CLOB转字符串问题。另⼀个是CLOB做等于不等于的⽐较出错问题。 第⼀个问题CLOB 转字符串问题,如果mybatis 的返回类型是⼀个Map.返回CLOB对应的字段类型是
anbase.jdbc.Clob
为了转字符串定义了⼀个Record类 继承 HashMap作为查询返回的对象。重新⾥⾯的put() get()⽅法。对于Clob类型的数据进⾏转换。public class DynamicRecord<K, V> extends HashMap<K, V> {
/**
*
*/
private static final long serialVersionUID = 3870456784929696124L;
@Override
public V put(K key, V value) {
if(value instanceof Clob){
Clob cvalue = (Clob) value;
value = (V) ClobToString(cvalue);
}
key = (String().toLowerCase();// 结果集所有key都转⼩写
return super.put(key, value);
}
@Override
public V get(Object key) {
V v = (key);
if (v != null)
return v;
(String().toUpperCase());// 由于关键字返回的结果集key都是⼤写,如果没到再转成⼤写⼀遍
}
public static String ClobToString(Clob clob) {
if(clob == null){
return null;
}
String reString = "";
try {
//mysql处理clob转string的⽅式
reString = SubString(1, (int) clob.length());
} catch (SQLException e) {
e.printStackTrace();
}
return reString;
}
}
CLOB做等于不等于的⽐较 出错的问题 由于我们⽐较的值是⼀个确定的。我们这⾥直接⽤的⼀个函数dbms_lob.substr(),这个函数如果字段长度过长会截取⼀部分出来。由于我们的字段很短。⽤这个没有问题。
总结
最后总结⼀下适配不同数据库基本也就这些思路,还有⼀点就是在开发过程中,不要过度依赖数据库独有的特性,尽量少的使⽤数据库函数,存储过程这些东西。这样会导致数据库迁移起来⾮常⿇烦。⽬前就这么多后续可能会有补充。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论