2016-12-01 33 views
6

Mam bazę danych MySQL 5.7 z kilkoma tabelami przechowującymi identyfikatory UUID w kolumnach z typem danych VARCHAR. Próbuję przekonwertować naszą podstawę kodu, aby użyć wersji Slick 3.1.1, ale mam problem z UUID do konwersji String. To jest moja definicja kolumna:Jak używać UUID w kolumnie VARCHAR ze Slick?

def myCol: Rep[UUID] = column[UUID]("myCol", SqlType("VARCHAR")) 

Kiedy biegnę zapytania otrzymuję wyjątki takie jak ten:

Got the exception java.sql.SQLException: Incorrect string value: '\xDA\xFD\xDAuOL...' for column 'myCol' at row 1 

Z tego co rozumiem, Slick zakłada, że ​​UUID powinien być przechowywany jako typy kolumn binarny tak próbowałem niejawnie przekonwertować na ciąg przy użyciu:

implicit val uuidToString = MappedColumnType.base[UUID, String](_.toString, UUID.fromString) 
def myCol: Rep[UUID] = column[UUID]("myCol", SqlType("VARCHAR"))(uuidToString) 

działa to do wkładania, ale kiedy wybrać wartości z myCol wyniki pochodzą pusty. Ktoś wie, jak zmusić Slicka do użycia VARCHAR zamiast BINARY (16) do konwersji danych?

EDIT:

Z niejawna to zapytanie nie zwróciło żadnych wyników:

db.run { table.filter(_.myCol === val)).result } 

Ale to się robi:

db.run { table.result }.map(_.filter(_.myCol == val)) 

EDIT2:

Mam mały projekt z demonstracją tutaj: https://github.com/nmatpt/slick-uuid-test

+0

Po prostu notatkę, ale można również przechowywać bity wysokiej/niskiej kolejności, ponieważ UUID to tylko dwie wartości "długie" (i powiązanie dwóch kolumn jako pojedynczego klucza unikalnego). Jeśli nadal chcesz używać łańcuchów, nie potrzebujesz 'VARCHAR' (jako ciąg UUID jest zawsze 36 znaków, 32 z usuniętymi myślnikami). – Rogue

Odpowiedz

3

Jesteś pewien, że puste wartości pochodzą z db? Właśnie sprawdziłem swoją implementację (w jednym z projektów, dokładnie to samo rozwiązanie) i wygląda na to, że działa doskonale. Upewnij się, że twoja kolumna fizyczna (w DB) jest faktycznie typu VARCHAR (a nie jakiegoś binarnego).

EDIT: OK, problem, który mamy jest to, że jako BINARYUUID definicja jest już zawarte w profilu MySQL. Szybkie (choć może trochę brudne) było przedefiniowanie globalnie byłoby tak:

package profile 

import java.sql.{PreparedStatement, ResultSet} 

import slick.ast._ 
import slick.jdbc.MySQLProfile 

object CustomMySqlProfile extends MySQLProfile { 
    import java.util.UUID 

    override val columnTypes = new JdbcTypes 

    class JdbcTypes extends super.JdbcTypes { 
    override val uuidJdbcType = new UUIDJdbcType { 
     override def sqlTypeName(sym: Option[FieldSymbol]) = "UUID" 
     override def valueToSQLLiteral(value: UUID) = "'" + value + "'" 
     override def hasLiteralForm = true 

     override def setValue(v: UUID, p: PreparedStatement, idx: Int) = p.setString(idx, toString(v)) 
     override def getValue(r: ResultSet, idx: Int) = fromString(r.getString(idx)) 
     override def updateValue(v: UUID, r: ResultSet, idx: Int) = r.updateString(idx, toString(v)) 

     private def toString(uuid: UUID) = if(uuid != null) uuid.toString else null 
     private def fromString(uuidString: String) = if(uuidString != null) UUID.fromString(uuidString) else null 
    } 
    } 
} 

a następnie importowania go zamiast zwykłej api:

import profile.CustomMySqlProfile.api._ 

To na pewno działa - ale należy pamiętać, że teraz UUID zostanie zmapowany na ciąg znaków w całej aplikacji (zamiast domyślnego pliku binarnego). Zasadniczo potrzebowałem przesłonić profil tylko dlatego, że wbudowano konwersję UUID. Normalnie używałbyś odwzorowań typów.

Podobna sprawa została rozwiązana w podobny sposób tutaj: https://github.com/slick/slick/issues/1446 (również UUID, ale dla sterownika Oracle).

+0

Co masz na myśli, mówiąc: "wyniki są puste"? Wyjątek powinien zostać prawdopodobnie zgłoszony, jeśli pole to będzie puste (ponieważ konwertowałbyś 'UUID.fromString' z nieprawidłową wartością). Warto podzielić odwzorowanie na osobne funkcje (jedna osobna funkcja do odwzorowywania UUID -> Ciąg i jeden ciąg -> UUID.W drugim można dodać punkty logowania/punkty przerwania, aby zobaczyć, co się stanie. –

+0

Pierwsze zapytanie wydaje się być dobre. Czy wygenerowany kod SQL jest poprawny? (możesz to zobaczyć, dodając następujący wiersz do twojego logback.xml -> '' –

+0

OK, mam pomysł. niejawne mapowanie w zakresie w miejscu, w którym wykonywane jest zapytanie - to: 'db.run {table.result} .map (_. filter (_. myCol == val))'. Jeśli nie - upewnij się, że masz to w zakresie –