import z bazy z błędnym kodowaniem

Ostatnio musiałem importować dane z bazy danych z kodowaniem w której zachodnioeuropejskiego w której były przechowywane polskie znaki. Było z tym trochę problemów, więc opiszę rozwiązanie

Zagadnienie Baza A – źródłowa, u klienta. Kodowanie zachodnioeuropejskie WE8DEC. Przechowywane są w niej jednak polskie znaki w kodowaniu EE8ISO8859P2 czyli ISO-8859-2. Takie kodowanie stosuje klient bazy. Oracle pozwala na takie kombinacje. Miałem kiedyś do czynienia z bazą danych w kodowaniu US7ASCII, która też przechowywała polskie znaki, mimo że to kodowanie zawiera tylko na znaki do kodu 127 a więc bez narodowych. Można zapisywać lub odczytywać dane z poziomu klienta tak aby konwersje nie miały miejsca – dzięki temu obce kodowanie nie przeszkadza. Problem pojawił się przy dostępie przez DB linki, ale o tym za chwilę. Baza B – docelowa, u nas. Optymalne kodowanie AL32UTF8 pozwalające przechowywać wszystkie znaki łącznie ze wschodnioazjatyckimi.

Próba rozwiązania Pierwsza próba importu polegała na wykonaniu insert into /tabela z B/ select … from /tabela z A/. Przy takiej operacji Oracle robił konwersję znaków z kodowania zachodnioeuropejskiego na unicode. W efekcie otrzymywaliśmy “krzaczki”, ale były to różne “krzaczki”, tak się przynajmniej na początkowo wydawało. Skoro literka Ą ma w kodowaniu ISO 8859-2 kod 0xA1 a pod tym kodem w ISO 8859-1 widnieje znak ¡, więc importował się np. ZWI¡ZEK. Wydawało się, że wystarczy tylko dopisać funkcję konwertującą odpowiedni “krzaczek” na odpowieni polski znak i będzie po kłopocie. Okazało się jednak, że literki Ś i Ż konwertują się na ten sam symbol � oznaczający nieznany znak. Rozwiązanie Pojawił się pomysł, aby funkcją utl_raw.cast_to_raw skonwertować oryginalny VARCHAR2 z systemu A na tym RAW, potem zaimportować do B i tam funkcją utl_raw.cast_to_varchar2 przekształcić z RAW na VARCHAR2 omijając po drodze konwersję. Okazało się jednak, że podczas odwoływania się z B do A poprzez dblink konwersja następuje przed wywołaniem  utl_raw.cast_to_raw i to mimo wymuszenia, aby funkcja się wykonywała na zdalnym serwerze. Konieczne okazało się utworzenie na A widoków zwracających wynik operacji utl_raw.cast_to_raw na polach z polskimi znakami. Dzięki temu poprzez dblink importowane są z A do B tablice bajtów typu RAW. Potem można w B przekonwertować wpis z RAW do VARCHAR2, ale najpierw trzeba przekształcić odebraną tablicę bajtów aby tekst zapisany w niej w kodowaniu EE8ISO8859P2 został przekształcony na tekst zapisany w kodowaniu bazy B – AL32UTF8. Polecenie wygląda więc następująco utl_raw.cast_to_varchar2(utl_raw.convert(kolumna_z_polskimi_znakami, ‘AMERICAN_AMERICA.AL32UTF8’, ‘AMERICAN_AMERICA.EE8ISO8859P2’)) i rozwiązało problem konwersji.

You May Also Like

Inconsistent Dependency Injection to domains with Grails

I've encountered strange behavior with a domain class in my project: services that should be injected were null. I've became suspicious as why is that? Services are injected properly in other domain classes so why this one is different?

Constructors experiment

I've created an experiment. I've created empty LibraryService that should be injected and Book domain class like this:

class Book {
def libraryService

String author
String title
int pageCount

Book() {
println("Finished constructor Book()")
}

Book(String author) {
this()
this.@author = author
println("Finished constructor Book(String author)")
}

Book(String author, String title) {
super()
this.@author = author
this.@title = title
println("Finished constructor Book(String author, String title)")
}

Book(String author, String title, int pageCount) {
this.@author = author
this.@title = title
this.@pageCount = pageCount
println("Finished constructor Book(String author, String title, int pageCount)")
}

void logInjectedService() {
println(" Service libraryService is injected? -> $libraryService")
}
}
class LibraryService {
def serviceMethod() {
}
}

Book has 4 explicit constructors. I want to check which constructor is injecting dependecies. This is my method that constructs Book objects and I called it in controller:

class BookController {
def index() {
constructAndExamineBooks()
}

static constructAndExamineBooks() {
println("Started constructAndExamineBooks")
Book book1 = new Book().logInjectedService()
Book book2 = new Book("foo").logInjectedService()
Book book3 = new Book("foo", 'bar').logInjectedService()
Book book4 = new Book("foo", 'bar', 100).logInjectedService()
Book book5 = new Book(author: "foo", title: 'bar')
println("Finished constructor Book(Map params)")
book5.logInjectedService()
}
}

Analysis

Output looks like this:

Started constructAndExamineBooks
Finished constructor Book()
Service libraryService is injected? -> eu.spoonman.refaktor.LibraryService@2affcce2
Finished constructor Book()
Finished constructor Book(String author)
Service libraryService is injected? -> eu.spoonman.refaktor.LibraryService@2affcce2
Finished constructor Book(String author, String title)
Service libraryService is injected? -> null
Finished constructor Book(String author, String title, int pageCount)
Service libraryService is injected? -> null
Finished constructor Book()
Finished constructor Book(Map params)
Service libraryService is injected? -> eu.spoonman.refaktor.LibraryService@2affcce2

What do we see?

  1. Empty constructor injects dependencies.
  2. Constructor that invokes empty constructor explicitly injects dependencies.
  3. Constructor that invokes parent's constructor explicitly does not inject dependencies.
  4. Constructor without any explicit call declared does not call empty constructor thus it does not inject dependencies.
  5. Constructor provied by Grails with a map as a parameter invokes empty constructor and injects dependencies.

Conclusion

Always explicitily invoke empty constructor in your Grail domain classes to ensure Dependency Injection! I didn't know until today either!

Me on Hadoop on Parleys

Finally I've managed to import my WarJUG presentation to parleys.com. See for yourself :) If you've got problems with opening the parleys' version try the ones uploaded to youtube. Here is part 1: And here is part 2: Finally I've managed to import my WarJUG presentation to parleys.com. See for yourself :) If you've got problems with opening the parleys' version try the ones uploaded to youtube. Here is part 1: And here is part 2: