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:
- 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.
- Ich habe versucht, die serialisierbare, zusammengesetzte ID zu entfernen, und so ziemlich alles andere, was ich finden konnte, machte das komplizierter.
- Ich mache einen Grails clean-all Befehl und lösche das Zielverzeichnis
- 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.
Haben Sie mit 'sql.commit()' nach 'sql.executeInsert (nativeInsert) 'versucht? – droggo
@ 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. –
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. –