mydql--oracle项目迁移简说

前段时间,花费一个月在公司做了一个项目迁移,在这里把自己的经验留下来。

这是第一次,做数据库的迁移,所以,决定记录下来这样一个历程。

 

环境: eclipse  3.0

框架结构:WSI (webwork + spring + ibaits)

  

1 数据结构迁移。

 

   在这里介绍一个工具,oracle官网可以下到,SQLDEVELOPER,现在的版本已经到了3.4或者4.0.,但是我推荐用的1.2,因为版本虽低,但是用于数据库迁移却很方便。

   第一步,需要导入mysql的驱动

  Tools-->preferences:

  

   然后建立连接:

           

接下来就是进行迁移了,迁移有两种方式:

第一种: 普通迁移

   

    然后选择表或者数据库

   

然后确认迁移,接下来,你可以自己确定转换的类型,且会生成相应的ddl文件。这种方法也会把相应的索引迁移过去。

如果,如果对于mysql和oracle类型结构不是很清楚的话,在Oracle官网的文档里面也会有比较,当然,在日志的最后我也会复制上去。

在我的项目中,只用到了下面8中类型:

变量 

Mydql

Oracle

字符型

Varchar

Varchar2

Char型

Char

         Varchar2  

整型

Int

Number[10,0]

浮点型

Double

Float[24,0]

中等长度

Mediumblob

Long raw

文本

Text

Clob

长文本

MediunText

Clob

日期

Date

Date

第二种是快速迁移:

 

填写好所弹出来的窗口的STEP1-STEP4,点击确认即可。

2   数据迁移

 2.1    表名去除单引号

很多时候,为了防止关键字的情况下,在往mysql插入数据时,往往会把表名放在单引号里,而在oracle里面是行不通的、所以,第一步,就是需要把所有的单引号去掉。

 2.2    日期转换问题

在oracle里面,date型数据插入是一个很大的问题,日期格式默认情况下是‘04-9月 -01’,

而我们的数据却是’2011-11-11 ’格式的,怎么处理呢?

大多数人会想到to_date函数

to_date(‘2011-11-11’,’yyyy-MM-dd’);

当然,在数据量不大的情况下,这种方法是可行了,可是,如果你需要插入的是好几万,甚至是几十万书句的时候,这种方式就行不通了,在这里就说说第二种方法。

改变oracle日期的默认格式。

Alter session set nls_date_format = ‘yyyy-MM-dd hh24:mi:ss’

注意的是,这只是暂时保存在session作用域,当你一旦关闭的时候也就得重新执行一次。

 2.3   &问题

在oracle里面,&是表示用户输入的标识,而在实际情况中,想text里面插入值得时候,很有可能是大片文章,那么,相对应的,也就可能出现了&nbsp &quot等符号的标识,我采取的方法是把所有这种标识去掉。

而如果不想去掉的话,可以采用[]把&包起来,也可达到相同的效果。

最后,关于数据迁移,我的建议是,用oracle自带的sqlplus,数据量过大时,第三方工具操作起来也相应的慢。

3     项目迁移

3.1   更改数据库连接配置

 jdbc.driverClassName=oracle.jdbc.driver.OracleDriver

jdbc.url=jdbc\:oracle\:thin\:@localhost\:1521\:ID

 3.2  SQL文的改变

总的概括:函数、limit分页、视图和索引、GROOUP BY

关于函数:

    3.2.1 DATEDIFF

      A  用trunc代替

      B  自己写函数(示例)

Java代码  
  1. /*  
  2. --功能:将字符串转为年月日格式,删除时分秒.  
  3. --来源:http://jorkin.reallydo.com/article.asp?id=529  
  4. --参数:  
  5.        Datechar Varchar2  
  6. --Oracle9i测试通过  
  7. */  
  8.   
  9. Create Or Replace Function CDate(Datechar In Varchar2) Return Date Is   
  10.      ReallyDo Date;   
  11. Begin   
  12.     Select to_date(to_char(to_date(to_char(Datechar), 'YYYY-MM-DD HH24:MI:SS'),   
  13.                            'YYYY-MM-DD'),   
  14.                    'YYYY-MM-DD')   
  15.     Into ReallyDo   
  16.     From Dual;   
  17.     Return(ReallyDo);   
  18. End CDate;   
  19.   
  20. /*  
  21. --功能:将字符串转为年月日时分秒格式.  
  22. --来源:http://jorkin.reallydo.com/article.asp?id=529  
  23. --参数:  
  24.        Datechar Varchar2  
  25. --Oracle9i测试通过  
  26. */  
  27.   
  28. Create Or Replace Function CDateTime(Datechar In Varchar2) Return Date Is   
  29.      ReallyDo Date;   
  30. Begin   
  31.     Select to_date(to_char(to_date(to_char(Datechar), 'YYYY-MM-DD HH24:MI:SS'),   
  32.                            'YYYY-MM-DD HH24:MI:SS'),   
  33.                    'YYYY-MM-DD HH24:MI:SS')   
  34.     Into ReallyDo   
  35.     From Dual;   
  36.     Return(ReallyDo);   
  37. End CDateTime;   
  38.   
  39. /*  
  40. --功能:类似MSSQL的日期比较函数  
  41. --来源:http://jorkin.reallydo.com/article.asp?id=529  
  42. --更新时间:20080721  
  43. --参数:  
  44.        Datepart   Varchar2 -- 比较年?月?日?  
  45.        StartDate Varchar2 -- 起始日期  
  46.        EndDate    Varchar2 -- 结束日期  
  47. --Oracle9i测试通过  
  48. */  
  49.   
  50. Create Or Replace Function Datediff   
  51. (   
  52.     Datepart  In Varchar2,   
  53.      StartDate In Varchar2,   
  54.      EndDate   In Varchar2   
  55. ) Return Number Is   
  56.      ReallyDo Numeric;   
  57. Begin   
  58.     Select Case Upper(Datepart)   
  59.                When 'YYYY' Then   
  60.                  Trunc(Extract(Year From CDate(EndDate)) -   
  61.                        Extract(Year From CDate(StartDate)))   
  62.                When 'M' Then   
  63.                 Datediff('YYYY', StartDate, EndDate) * 12 +   
  64.                  (Extract(Month From CDate(EndDate)) -   
  65.                   Extract(Month From CDate(StartDate)))   
  66.                When 'D' Then   
  67.                  Trunc(CDate(EndDate) - CDate(StartDate))   
  68.                When 'H' Then   
  69.                 Datediff('D', StartDate, EndDate) * 24 +   
  70.                  (to_Number(to_char(CDateTime(EndDate), 'HH24')) -   
  71.                   to_Number(to_char(CDateTime(StartDate), 'HH24')))   
  72.                When 'N' Then   
  73.                 Datediff('D', StartDate, EndDate) * 24 * 60 +   
  74.                  (to_Number(to_char(CDateTime(EndDate), 'MI')) -   
  75.                   to_Number(to_char(CDateTime(StartDate), 'MI')))   
  76.                When 'S' Then   
  77.                 Datediff('D', StartDate, EndDate) * 24 * 60 * 60 +   
  78.                  (to_Number(to_char(CDateTime(EndDate), 'SS')) -   
  79.                   to_Number(to_char(CDateTime(StartDate), 'SS')))   
  80.                Else   
  81.                 -29252888  
  82.            End   
  83.     Into ReallyDo   
  84.     From Dual;   
  85.     Return(ReallyDo);   
  86. End Datediff;  
/*
--功能:将字符串转为年月日格式,删除时分秒.
--来源:http://jorkin.reallydo.com/article.asp?id=529
--参数:
       Datechar Varchar2
--Oracle9i测试通过
*/

Create Or Replace Function CDate(Datechar In Varchar2) Return Date Is
     ReallyDo Date;
Begin
    Select to_date(to_char(to_date(to_char(Datechar), 'YYYY-MM-DD HH24:MI:SS'),
                           'YYYY-MM-DD'),
                   'YYYY-MM-DD')
    Into ReallyDo
    From Dual;
    Return(ReallyDo);
End CDate;

/*
--功能:将字符串转为年月日时分秒格式.
--来源:http://jorkin.reallydo.com/article.asp?id=529
--参数:
       Datechar Varchar2
--Oracle9i测试通过
*/

Create Or Replace Function CDateTime(Datechar In Varchar2) Return Date Is
     ReallyDo Date;
Begin
    Select to_date(to_char(to_date(to_char(Datechar), 'YYYY-MM-DD HH24:MI:SS'),
                           'YYYY-MM-DD HH24:MI:SS'),
                   'YYYY-MM-DD HH24:MI:SS')
    Into ReallyDo
    From Dual;
    Return(ReallyDo);
End CDateTime;

/*
--功能:类似MSSQL的日期比较函数
--来源:http://jorkin.reallydo.com/article.asp?id=529
--更新时间:20080721
--参数:
       Datepart   Varchar2 -- 比较年?月?日?
       StartDate Varchar2 -- 起始日期
       EndDate    Varchar2 -- 结束日期
--Oracle9i测试通过
*/

Create Or Replace Function Datediff
(
    Datepart  In Varchar2,
     StartDate In Varchar2,
     EndDate   In Varchar2
) Return Number Is
     ReallyDo Numeric;
Begin
    Select Case Upper(Datepart)
               When 'YYYY' Then
                 Trunc(Extract(Year From CDate(EndDate)) -
                       Extract(Year From CDate(StartDate)))
               When 'M' Then
                Datediff('YYYY', StartDate, EndDate) * 12 +
                 (Extract(Month From CDate(EndDate)) -
                  Extract(Month From CDate(StartDate)))
               When 'D' Then
                 Trunc(CDate(EndDate) - CDate(StartDate))
               When 'H' Then
                Datediff('D', StartDate, EndDate) * 24 +
                 (to_Number(to_char(CDateTime(EndDate), 'HH24')) -
                  to_Number(to_char(CDateTime(StartDate), 'HH24')))
               When 'N' Then
                Datediff('D', StartDate, EndDate) * 24 * 60 +
                 (to_Number(to_char(CDateTime(EndDate), 'MI')) -
                  to_Number(to_char(CDateTime(StartDate), 'MI')))
               When 'S' Then
                Datediff('D', StartDate, EndDate) * 24 * 60 * 60 +
                 (to_Number(to_char(CDateTime(EndDate), 'SS')) -
                  to_Number(to_char(CDateTime(StartDate), 'SS')))
               Else
                -29252888
           End
    Into ReallyDo
    From Dual;
    Return(ReallyDo);
End Datediff;

          此段代码来源:http://Jorkin.Reallydo.Com/default.asp?id=529

    3.2.2 系统日期函数

       Mysql : select sysdate()

       Oracle : select sysdate from dual;

                            …..

在此省略一步分函数,因为mysql函数比较强大,所以具体的时候碰到了网上搜索对应的方案把。比如date_add等等。

         关于 limit

                    Limit是mysql自带的分页机制,当迁移到oracle里面来,也有两种处理机制。

               A 简单的可以用 rownum代替

                  示例: 查询emp表中的前三行

Sql代码  
  1. select s.* from (select t.*,rownum rn from emp t) s     
  2.               where s.rn <= 3     
select s.* from (select t.*,rownum rn from emp t) s  
              where s.rn <= 3   

 

     B 写一个oracle分页函数,这个大家肯定都用过吧。

         关于索引和是视图

            索引没什么变化,直接拿过来运行就OK了。

            视图的语法不同,

            在mysql中:

              CREATE ALGORITHM=UNDEFINED DEFINER=`username`@`%` SQL SECURITY DEFINER VIEW viewname

             Oracle: create or replace view viewname

         关于group by 

              在oracle里面,有个规定,含有group by的查询语句,字段类不在聚合函数中,就必须在group by语句后,我的说法是,你把字段列加到group by 后面就可以了。

  下面说个insert和update的问题:

 

向一个表中执行update和insert操作时,类型为CLOB列的就出现异常

异常为:

仅可以为插入 LONG 列的 LONG 值赋值 CLOB

............

当时上网查了,有很多人也碰到了相同的问题,总体而言有两种解决方式,

第一种是驱动版本过低。

第二种是修改hibernate的配置文件,当时,前提是你开发的持久层必须是hibernate。

我从oracle官方网站下了一个最新的JDBC驱动(通过附件上传了),运行还是报哪个错误。没有效果

而第二种方法,对于我而言,是肯定行不通的。

网上有部分人说,值长度在2000-4000之间会出现异常,

可是,我的不同,

我的是值的长度在1000-2000之间会出现异常,稍短或长都没问题。

在这里说明一下,肯定不会是因为长度限制的问题,因为CLOB在ORACLE里面,所输入的可达4G。

对于LONG,这个类型我没用过,在ORACLE里面也不建议用。是一个已经被废弃了的类型。

最后我的解决方法是,在视图层,对所输入的进行判断,把少了的用空格不上,在jsp中,四个空格等于一个字符的长度。

让其长度超过2000即可,但是,这种方法对于紧急情况可能还不错,而效率上就不说了。

还有,就是说,如果你的长度不会超过2000,就建议用varchar2,但是需要注意的是数据库字符集应该是UTF-8的,对于UTF-8或欧洲的某些字符集,oracle在存储时,对于一个字符需要2个或3个字节的存储空间,虽然表定义中varchar2(4000),但是其实该字段的data_length为其2倍或3倍长。

在这里,也顺便把在网上看到的,关于hibernate修改配置的方法,粘贴出来.

Hibernate實體對象中的数据成员类型为String,映射的数据库字段类型为org.springframework.orm.hibernate.support.ClobStringType 。實例如下:

Java代码 复制代码
  1. @SuppressWarnings" serial " )   
  2.  @Entity   
  3.  @Table(name = " GUIDE " )    
  4.  // @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)    
  5.  public class Guide {   
  6.         
  7.       @Type(type = " org.springframework.orm.hibernate3.support.ClobStringType " )   
  8.       private String content; // 内容   
  9.     
  10.            // get & set    
  11.  }   
@SuppressWarnings( " serial " )
 @Entity
 @Table(name = " GUIDE " )
 // @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) 
 public class Guide {
     
      @Type(type = " org.springframework.orm.hibernate3.support.ClobStringType " )
      private String content; // 内容
 
           // get & set 
 } 

如果使用Spring的这个Clob类型就需要在applicationContext.xml中的sessionFactory bean里注入oracleLobHandler bean。下面給出Oracle數據庫的LobHandler配置

  1. < bean id ="oracleLobHandler"    
  2.         class ="org.springframework.jdbc.support.lob.OracleLobHandler"    
  3.         lazy-init ="true" >      
  4.       < property name ="nativeJdbcExtractor" ref ="nativeJdbcExtractor" />    
  5.  </ bean >        
  6.     
  7.  < bean id ="nativeJdbcExtractor"         class ="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"    
  8.         lazy-init ="true" />      
  9.     
  10.  <!-- Hibernate SessionFactory -->    
  11.  < bean id ="sessionFactory" class ="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" >    
  12.       < property name ="dataSource" ref ="dataSource" />    
  13.       < property name ="configLocation" value ="classpath:hibernate/hibernate.cfg.xml" />    
  14.       < property name ="hibernateProperties" >    
  15.           < value >    
  16.                
  17.           </ value >    
  18.       </ property >    
  19.       < property name ="lobHandler" ref ="oracleLobHandler" />    
  20.  </ bean >   
< bean id ="oracleLobHandler" 
        class ="org.springframework.jdbc.support.lob.OracleLobHandler" 
        lazy-init ="true" >   
      < property name ="nativeJdbcExtractor" ref ="nativeJdbcExtractor" /> 
 </ bean >     
 
 < bean id ="nativeJdbcExtractor"         class ="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor" 
        lazy-init ="true" />   
 
 <!-- Hibernate SessionFactory --> 
 < bean id ="sessionFactory" class ="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" > 
      < property name ="dataSource" ref ="dataSource" /> 
      < property name ="configLocation" value ="classpath:hibernate/hibernate.cfg.xml" /> 
      < property name ="hibernateProperties" > 
          < value > 
            
          </ value > 
      </ property > 
      < property name ="lobHandler" ref ="oracleLobHandler" /> 
 </ bean > 

 

 

LobHandler 需要访问本地 JDBC 对象,这一任务委托给 NativeJdbcExtractor Bean 来完成,NativeJdbcExtractor是一個本地JDBC對象抽取器,因此我們为 LobHandler 注入了一个 nativeJdbcExtractor。最后,我们把 lobHandler Bean 注入到需要进行 LOB 数据访问操作的sessionFactory Bean中或者直接注入到dao Bean中去。

大家可能已经注意到 nativeJdbcExtractor 和 oracleLobHandler Bean 都设置为 lazy-init="true",这是因为 nativeJdbcExtractor 需要通过运行期的反射机制获取底层的JDBC 对象,所以需要避免在 Spring 容器启动时就实例化这两个Bean。
如果底層數據庫是 DB2、SQL Server、MySQL 等非 Oracle 的其它數據庫,则只要简单配置一个 DefaultLobHandler 就可以了,如下所示:
 
 
 
  1. < bean id ="defaultLobHandler"    
  2.  class ="org.springframework.jdbc.support.lob.DefaultLobHandler"    
  3.  lazy-init ="true" />   
  4.   
  5.  < bean id ="testDao" class ="com.test.dao.jdbc.TestJdbcDao" >    
  6.      < property name ="lobHandler" ref =" defaultLobHandler" />    
  7.  < property name ="jdbcTemplate" ref ="jdbcTemplate" />    
  8.  </ bean >   
< bean id ="defaultLobHandler" 
 class ="org.springframework.jdbc.support.lob.DefaultLobHandler" 
 lazy-init ="true" />

 < bean id ="testDao" class ="com.test.dao.jdbc.TestJdbcDao" > 
     < property name ="lobHandler" ref =" defaultLobHandler" /> 
 < property name ="jdbcTemplate" ref ="jdbcTemplate" /> 
 </ bean > 

DefaultLobHandler 只是简单地代理标准 JDBC 的 PreparedStatement 和 ResultSet 对象,由于并不需要访问数据库驱动本地的 JDBC 对象,所以它不需要 NativeJdbcExtractor 的帮助。

 希望总有一张方法能帮你解决掉麻烦的ORA-01461

如果,哪位有更好的解决方法,就麻烦分享一下了。

还有,就是说,如果你的长度不会超过2000,就建议用varchar2,但是需要注意的是数据库字符集应该是UTF-8的,对于UTF-8或欧洲的某些字符集,oracle在存储时,对于一个字符需要2个或3个字节的存储空间,虽然表定义中varchar2(4000),但是其实该字段的data_length为其2倍或3倍长。

.............

4 旧数据导入问题

   对于一个数据库项目迁移工作,数据迁移也是一个难点,

   我就说说我碰到的问题。

   在mysql中一个字段类型为text,里面的数据多的有几万个字符,而mysql所导出来的东西又仅仅是.sql文件,而在oracle里面,任何一个字符串通过insert语句插入值时,一旦超过4000个字符,就会报错。

  这里不多说,相信碰到的人都知道,如果你是几条数据好几个方法都能解决,可是如果你碰到了好几万条数据的话,就会有点手足无措了,这里我最后是通过把原来的数据导出成xml文档,然后通过一个简单的java程序进行插入值。具体代码就不贴了,需要的博友可以通过留言,我给你。

总的来说,最主要的就是sql问题,认真仔细点就好。关于迁移,是一个工作量比较大,但是技术含量比较低的工作,所以,每一步走稳,每一步走好是最重要的。

posted on 2011-06-09 14:58  candiceChan  阅读(563)  评论(0编辑  收藏  举报

导航