2016-05-12 20 views
0

TorGrails Integration Testdaten können nicht kreuzen nativen SQL im Vergleich zu GORM Grenze

Bei Integrationstests laufen, ich mag die Datenbank mit voreingestellten Daten füllen GORM verwenden.

Problem

Für bestimmte Domänen können nativen SQL-Abfragen Daten nicht von GORM und umgekehrt (GORM Abfragen nicht sehen können Daten eingefügt durch native SQL) eingefügt sehen. Innerhalb eines einzigen Laufs hat eine Domäne dieses Problem und eine andere nicht.

Beschreibung

Bitte beachten Sie die Konsolenausgabe in der Nähe der Unterseite dieses Beitrags für eine klare Beschreibung. Ich denke, Hinweise auf die Lösung können in der Ausgabe gesehen werden.

Projekt: Grails 2.4.2 Datenquelle:

test { 
    cehCode = "PR" 
    schemaName = "AF" 
    parallelSchemaName = "AF" 
    dataSource { 
     dbCreate = "create-drop" 
     url = "jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE;INIT=CREATE SCHEMA IF NOT EXISTS AF" 
     driverClassName = "org.h2.Driver" 
     properties { 
      initialSize = 2 
      minIdle = 1 
      maxIdle = 2 
      maxActive = 2 
      maxWait = 2000 
      maxAge = 60000 
      minEvictableIdleTimeMillis=30000 
      timeBetweenEvictionRunsMillis=30000 
      abandonWhenPercentageFull = 50 
      numTestsPerEvictionRun=3 
      testOnBorrow=true 
      testWhileIdle=true 
      testOnReturn=true 
      validationQuery="SELECT 1" 
      validationInterval=500 
      //defaultTransactionIsolation = java.sql.Connection.TRANSACTION_READ_UNCOMMITTED 
     } 
    } 

}

Integrationstest:

void "Test CUST filter"() { 
    when: 'Build test data' 
    then: 
     assert testQueryService.testInsert() == "Done with inserts" 
     assert testQueryService.testQuery() == "Done with queries" 
} 

Service:

package com.lrs.accrual.common 

import com.lrs.contract.TFastCodes 
import grails.transaction.Transactional 
import groovy.sql.Sql 
import com.lrs.accrual.IntermodalRatingAuth 

@Transactional 
class TestQueryService { 
def sessionFactory 

def testInsert() { 
    println "----------------------START EXECUTING INSERTS ON TABLES------------------------------" 
    println "********* START NEW TABLE/DOMAIN TFAST_CODES/TFastCodes *********" 
    def nativeInsert = """INSERT INTO AF.TFAST_CODES (APPL_ELE,APPL_CD_VAL,APPL_CD_DESC,CAS_OWNER,ACS_TYPE_CD) 
       VALUES ('NATIVE SQL2','1','ORPT-TEST  PR','ZZZZ','BU')""" 
    def sql = new Sql(sessionFactory.getCurrentSession().connection()) 
    sql.executeInsert(nativeInsert) 
    sql.commit() 
    TFastCodes codes = new TFastCodes(applicationElement: 'GORM new 3', casOwner: 'ZZZZ', applicationCodeDescription: 'JUNK2', applicationCodeValue: '1', accessTypeCode: 'BU', newColumn2: 'Val') 
    println "Valid TFastCodes domain? " + codes.validate() 
    codes.save(flush: true) 
    println "********* END NEW TABLE/DOMAIN *********" 

    println "********* START OLD TABLE/DOMAIN TIMDL_RT_AUTH/IntermodalRatingAuth *********" 
    def nativeInsert2 = """INSERT INTO AF.TIMDL_RT_AUTH (AGREEMENT_NUMB,MRKT_RATE_KEY,WB_RT_AUTH_REF) 
       VALUES ('NATIVE',1,'2016-01-01-01.01.01.000000')""" 
    def sql2 = new Sql(sessionFactory.getCurrentSession().connection()) 
    sql2.executeInsert(nativeInsert2) 
    sql2.commit() 
    IntermodalRatingAuth auth= new IntermodalRatingAuth(agreementNumber:"GORM", marketingRateKey:1, waybillRateAuthRef: "other string") 
    println "Valid IntermodalRatingAuth domain? " + auth.validate() 
    auth.save(flush: true) 
    println "********* END OLD TABLE/DOMAIN *********" 
    println "----------------------END EXECUTING INSERTS ON TABLES------------------------------" 
    return "Done with inserts" 
} 

def testQuery() { 
    println "----------------------START EXECUTING QUERIES ON TABLES------------------------------" 
    println "********* START QUERIES AGAINST NEW TABLE/DOMAIN TFAST_CODES/TFastCodes *********" 
    def codes = TFastCodes.list() 
    codes?.eachWithIndex { val, i -> println "Gorm TFastCodes result $i: " + val.properties } 
    println "Native TFAST_CODES query result: " + sessionFactory.getCurrentSession()?.createSQLQuery("SELECT * FROM AF.TFAST_CODES").list() 
    println "********* END QUERIES AGAINST NEW TABLE/DOMAIN TFAST_CODES/TFastCodes *********" 
    println "********* START QUERIES AGAINST OLD TABLE/DOMAIN TIMDL_RT_AUTH/IntermodalRatingAuth *********" 
    def auths = IntermodalRatingAuth.list() 
    auths?.eachWithIndex { val, i -> println "Gorm IntermodalRatingAuth result $i: " + val.properties } 
    println "Native TIMDL_RT_AUTH query result: " + sessionFactory.getCurrentSession()?.createSQLQuery("SELECT * FROM AF.TIMDL_RT_AUTH").list() 
    println "********* END QUERIES AGAINST OLD TABLE/DOMAIN TIMDL_RT_AUTH/IntermodalRatingAuth *********" 
    println "----------------------END EXECUTING QUERIES ON TABLES------------------------------" 
    return "Done with queries" 
} 
} 

Domain von Grund auf neu erstellt (Werke):

class TFastCodes implements Serializable { 

String applicationElement 
String applicationCodeValue 
String applicationCodeDescription 
String casOwner 
String accessTypeCode 
static mapping = { 
    table 'AF.TFAST_CODES' 
    version false 
    id composite: ['applicationElement', 'applicationCodeValue'] 
    applicationElement column:'APPL_ELE' 
    applicationCodeValue column:'APPL_CD_VAL' 
    applicationCodeDescription column:'APPL_CD_DESC' 
    casOwner column:'CAS_OWNER' 
    accessTypeCode column:'ACS_TYPE_CD' 
} 

static constraints = { applicationElement(blank:false) 
         applicationCodeValue(blank:false)} 

} 

Domäne Bestehende (funktioniert nicht):

class IntermodalRatingAuth { 

String agreementNumber=LRAccrualConstants.STRING_EMPTY 
Integer marketingRateKey= LRAccrualConstants.ZERO 
String waybillRateAuthRef 

static mapping = { 
    table 'AF.TIMDL_RT_AUTH' 
    version false 
    agreementNumber column:'AGREEMENT_NUMB' 
    marketingRateKey column:'MRKT_RATE_KEY' 
    waybillRateAuthRef column:'WB_RT_AUTH_REF' 
} 
// Read-only. No constraints needed. 
} 

Konsolenausgabe:

----------------------START EXECUTING INSERTS ON TABLES------------------------------ 
********* START NEW TABLE/DOMAIN TFAST_CODES/TFastCodes ********* 
Valid TFastCodes domain? true 
Hibernate: select tfastcodes_.APPL_ELE, tfastcodes_.APPL_CD_VAL, tfastcodes_.ACS_TYPE_CD as ACS_TYPE3_37_, tfastcodes_.APPL_CD_DESC as APPL_CD_4_37_, tfastcodes_.CAS_OWNER as CAS_OWNE5_37_ from AF.TFAST_CODES tfastcodes_ where tfastcodes_.APPL_ELE=? and tfastcodes_.APPL_CD_VAL=? 
Hibernate: insert into AF.TFAST_CODES (ACS_TYPE_CD, APPL_CD_DESC, CAS_OWNER, APPL_ELE, APPL_CD_VAL) values (?, ?, ?, ?, ?) 
********* END NEW TABLE/DOMAIN ********* 
********* START OLD TABLE/DOMAIN TIMDL_RT_AUTH/IntermodalRatingAuth ********* 
Valid IntermodalRatingAuth domain? true 
********* END OLD TABLE/DOMAIN ********* 
----------------------END EXECUTING INSERTS ON TABLES------------------------------ 
----------------------START EXECUTING QUERIES ON TABLES------------------------------ 
********* START QUERIES AGAINST NEW TABLE/DOMAIN TFAST_CODES/TFastCodes ********* 
Hibernate: select this_.APPL_ELE as APPL_ELE1_37_0_, this_.APPL_CD_VAL as APPL_CD_2_37_0_, this_.ACS_TYPE_CD as ACS_TYPE3_37_0_, this_.APPL_CD_DESC as APPL_CD_4_37_0_, this_.CAS_OWNER as CAS_OWNE5_37_0_ from AF.TFAST_CODES this_ 
Gorm TFastCodes result 0: [applicationCodeDescription:ORPT-TEST  PR, applicationCodeValue:1, accessTypeCode:BU, applicationElement:NATIVE SQL2, casOwner:ZZZZ] 
Gorm TFastCodes result 1: [applicationCodeValue:1, accessTypeCode:BU, applicationElement:GORM new 3, casOwner:ZZZZ, applicationCodeDescription:JUNK2] 
Hibernate: SELECT * FROM AF.TFAST_CODES 
Native TFAST_CODES query result: [[NATIVE SQL2, 1, BU, ORPT-TEST  PR, ZZZZ], [GORM new 3, 1, BU, JUNK2, ZZZZ]] 
********* END QUERIES AGAINST NEW TABLE/DOMAIN TFAST_CODES/TFastCodes ********* 
********* START QUERIES AGAINST OLD TABLE/DOMAIN TIMDL_RT_AUTH/IntermodalRatingAuth ********* 
Gorm IntermodalRatingAuth result 0: [marketingRateKey:1, agreementNumber:GORM, waybillRateAuthRef:other string] 
Hibernate: SELECT * FROM AF.TIMDL_RT_AUTH 
Native TIMDL_RT_AUTH query result: [[1, NATIVE, 1, 2016-01-01-01.01.01.000000]] 
********* END QUERIES AGAINST OLD TABLE/DOMAIN TIMDL_RT_AUTH/IntermodalRatingAuth ********* 
----------------------END EXECUTING QUERIES ON TABLES------------------------------ 

Zusätzliche Details

In mein Grails-Projekt habe ich eine In-Memory-DB eingerichtet für die Testumgebung. Ich möchte eine Reihe von Testdaten jedes Mal frisch speichern, wenn spezifische Integrationstests ausgeführt werden.

Beim Speichern der Daten mit GORM (domain.save()) können die GORM-Abfragen im Service die Daten problemlos finden, die von den nativen SQL-Einfügungen eingefügten Daten können jedoch nicht angezeigt werden. Die nativen SQL-Abfragen von sessionFactory hingegen können die eingefügten GORM-Daten nicht sehen, sind jedoch in der Lage, die vom nativen SQL eingefügten Daten zu sehen.

Wie Sie unten sehen können, ich flush: True und habe jeden Befehl, den ich weiß, um den Datenlauf zu begehen versucht. Die meisten Domains, mit denen ich getestet habe, sind existierende Domains und sie funktionieren nie für sie. Wenn ich jedoch neue Domänen von Grund auf neu erstelle (mit dem Befehl "creates domain" von Grails), funktioniert es oft. Es scheint mir, dass das Problem tiefer in der Grails- oder Hibernate-Architektur liegt.Die Codebeispiele unten sind vereinfachte, aber ich habe einen Dienst, der eine alte Domain und neue Domain nebeneinander zeigt:

  • Beide sowohl von GORM eingefügt bekommen und nativen SQL (zwei Zeilen in jeder Tabelle)
  • Beide werden von GORM und nativem SQL abgefragt
  • Die alte Domäne zeigt native eingefügte Daten für die systemeigene Abfrage und GORM eingefügte Daten für die GORM-Abfrage
  • Die neue Domäne zeigt sowohl Ergebnisse für die native Abfrage und beide Ergebnisse für die GORM Abfrage

Wenn es funktioniert, funktioniert es für GORM und nativ. Wenn es nicht funktioniert, sieht keiner die Daten, die vom anderen eingefügt werden.

Debuggen Schritte habe ich gemacht:

  1. Die Datenquelle unten ist viel größer als das, was mit ich begann. Meine erste war sehr einfach, ohne AF-Schema, keine zusätzlichen Eigenschaften usw. Alles wurde hinzugefügt, um Fehler zu beheben.
  2. Ich habe versucht, die serialisierbare, zusammengesetzte ID zu entfernen, und so ziemlich alles andere, was ich finden konnte, machte das komplizierter.
  3. Ich mache einen Grails clean-all Befehl und lösche das Zielverzeichnis
  4. Ich habe Probleme beim Erstellen eines konsistenten Szenarios, das eine Domäne zu arbeiten beginnt, aber es ist in der Regel eine Kombination aus wechselnden Klasse, Verschieben von Paketen, Ändern des in der Domäne definierten Tabellennamens oder Erstellen einer vollständig neuen Domäne/Tabelle.
+0

Haben Sie mit 'sql.commit()' nach 'sql.executeInsert (nativeInsert) 'versucht? – droggo

+0

@ droggo, danke für deinen Kommentar. Ich habe die Commit-Anweisung hinzugefügt, wie Sie gesagt haben, und habe keine Änderung gesehen. Zur besseren Veranschaulichung habe ich auch die zweite Domäne hinzugefügt, die ich zuvor erwähnt habe, wo sie für die eine und nicht für die andere funktioniert und die Protokollierung verbessert. Zwei Dinge, auf die Sie hinweisen sollten: 1. Wie Sie sehen, findet TFAST_CODES immer noch beide INSERT-Anweisungen für jede Abfrage, aber TIMDL_RT_AUTH (alte Domäne) findet immernoch die eine. 2. Die Hibernate-Protokollanweisungen unterscheiden sich für die Einfügungen und Abfragen auf diesen. Anhaltspunkt für die Ursache? Nicht sicher. –

+0

Einige Gedanken: Sie können versuchen, Ihre Einfügungen [in einer Transaktion] (http://docs.grails.org/2.4.x/ref/Domain%20Classes/withTransaction.html) zu umbrechen, um sie vor diesen Abfragen zu speichern. Sie können auch [eine neue Sitzung] (http://docs.grails.org/2.4.x/ref/Domain%20Classes/withNewSession.html) verwenden, um den Status Ihrer Datenbank abzufragen und abzurufen. –

Antwort

0

Sergio/droggo,

Vielen Dank für Ihre Hilfe! Ich denke, ich habe es aufgespürt. Wie es oft der Fall ist, ist es schwierig, genau zu wissen, welcher Code geteilt werden muss, damit andere bei der Fehlersuche helfen können. Es scheint, dass das native SQL die Datenquellen-Konfiguration traf, aber die GORM/Domänen wurden mokiert, so wurden nicht auf die Datenquelle beibehalten. Ich habe das nicht bemerkt, weil ich ein Grails-Plugin namens build-test-data verwende, dessen @Build-Annotation ein Wrapper für die @Mock-Annotation ist (offensichtlich war mir das nicht bewusst). @Build ist nur für Komponententests gedacht, nicht für Integrationstests. Damit jemand mir helfen kann, müssten sie die Anmerkungen des Integrationstests sehen (siehe unten). Entschuldigung für den fehlenden Quellcode!

@TestFor(TestQueryService) 
@Build([ContractFilter, ContractHeader, Qualifier, Provision, TFastCodes]) 
class ContractFilterServiceSpec extends Specification {