#sql-server #hibernate #grails
#sql-server #переход в спящий режим #grails
Вопрос:
Я использую grails 3.3.9 с базой данных sql server. Я использую код, созданный по умолчанию для моего класса домена. Когда я захожу на страницу индекса, я получаю No Session found for current thread
ошибку. Для использования кода, созданного по умолчанию, я немного озадачен тем, почему это происходит. В Grails 2.x после создания каркаса приложение просто работает. В любом случае, вот мой код, и, надеюсь, кто-нибудь сможет пролить свет на то, чего я, очевидно, не знаю:
application.yml
hibernate:
cache:
queries: false
use_second_level_cache: false
use_query_cache: false
dataSource:
pooled: true
jmxExport: true
driverClassName: org.h2.Driver
username: sa
password: ''
environments:
development:
dataSource:
dbCreate: create-drop
url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
dataSources:
tst:
dbCreate: update
url: jdbc:sqlserver://TESTSERVER;databaseName=Technician;useNTLMv2=true
driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
dialect: org.hibernate.dialect.SQLServer2012Dialect
pooled: true
username: user
password: 'pass'
formatSql: true
logSql: true
test:
dataSource:
dbCreate: update
url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
production:
dataSource:
dbCreate: none
url: jdbc:h2:./prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
properties:
jmxEnabled: true
initialSize: 5
maxActive: 50
minIdle: 5
maxIdle: 25
maxWait: 10000
maxAge: 600000
timeBetweenEvictionRunsMillis: 5000
minEvictableIdleTimeMillis: 60000
validationQuery: SELECT 1
validationQueryTimeout: 3
validationInterval: 15000
testOnBorrow: true
testWhileIdle: true
testOnReturn: false
jdbcInterceptors: ConnectionState
defaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED
build.gradle
buildscript {
repositories {
mavenLocal()
maven { url "https://repo.grails.org/grails/core" }
}
dependencies {
classpath "org.grails:grails-gradle-plugin:$grailsVersion"
classpath "org.grails.plugins:hibernate5:${gormVersion-".RELEASE"}"
classpath "com.bertramlabs.plugins:asset-pipeline-gradle:2.15.1"
}
}
version "0.1"
group "tstsupport"
apply plugin:"eclipse"
apply plugin:"idea"
apply plugin:"war"
apply plugin:"org.grails.grails-web"
apply plugin:"asset-pipeline"
apply plugin:"org.grails.grails-gsp"
repositories {
mavenLocal()
maven { url "https://repo.grails.org/grails/core" }
}
dependencies {
compile "org.springframework.boot:spring-boot-starter-logging"
compile "org.springframework.boot:spring-boot-autoconfigure"
compile "org.grails:grails-core"
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "org.springframework.boot:spring-boot-starter-tomcat"
compile "org.grails:grails-web-boot"
compile "org.grails:grails-logging"
compile "org.grails:grails-plugin-rest"
compile "org.grails:grails-plugin-databinding"
compile "org.grails:grails-plugin-i18n"
compile "org.grails:grails-plugin-services"
compile "org.grails:grails-plugin-url-mappings"
compile "org.grails:grails-plugin-interceptors"
compile "org.grails.plugins:cache"
compile "org.grails.plugins:async"
compile "org.grails.plugins:scaffolding"
compile "org.grails.plugins:events"
compile "org.grails.plugins:hibernate5"
compile "org.hibernate:hibernate-core:5.1.16.Final"
compile "org.grails.plugins:gsp"
console "org.grails:grails-console"
profile "org.grails.profiles:web"
runtime "org.glassfish.web:el-impl:2.1.2-b03"
runtime "com.h2database:h2"
runtime "org.apache.tomcat:tomcat-jdbc"
runtime "com.bertramlabs.plugins:asset-pipeline-grails:2.15.1"
runtime "com.microsoft.sqlserver:mssql-jdbc:7.2.1.jre8"
testCompile "org.grails:grails-gorm-testing-support"
testCompile "org.grails.plugins:geb"
testCompile "org.grails:grails-web-testing-support"
testRuntime "org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.1"
testRuntime "net.sourceforge.htmlunit:htmlunit:2.18"
}
bootRun {
jvmArgs('-Dspring.output.ansi.enabled=always')
addResources = true
String springProfilesActive = 'spring.profiles.active'
systemProperty springProfilesActive, System.getProperty(springProfilesActive)
}
assets {
minifyJs = true
minifyCss = true
}
Класс домена: TST_Customer.groovy
package TSTSupport
class TST_Technician {
String techCode
String techName
boolean tec990Flag
boolean tecCCFlag
Date tecCreateDate
String tecCreatedBy
Date tecModifyDate
String tecModifiedBy
boolean tecActiveFlag
static constraints = {
techCode shared: "techCode"
techName blank: false, maxSize: 75
tecCreatedBy blank: false, maxSize: 35
tecModifyDate nullable: true
tecModifiedBy blank: false, maxSize: 35
}
static mapping = {
datasource "tst"
table name: "lkp_Technician", schema: "dbo", catalog: "Technician"
version false
id generator: 'assigned', name: 'techCode', type: 'string'
techCode column: '[TechnicianCode]'
techName column: '[TechnicianName]'
tec990Flag column: '[Tech990Flag]'
tecCCFlag column: '[TechCCFlag]'
tecCreateDate column: '[TechCreateDate]'
tecCreatedBy column: '[TechCreatedBy]'
tecModifyDate column: '[TechModifyDate]'
tecModifiedBy column: '[TechModifiedBy]'
tecActiveFlag column: '[TechActiveFlag]'
}
}
Контроллер
package TSTSupport
import grails.validation.ValidationException
import static org.springframework.http.HttpStatus.*
class TST_CustomerController {
TST_CustomerService TST_CustomerService
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond TST_CustomerService.list(params), model:[TST_CustomerCount: TST_CustomerService.count()]
}
def show(Long id) {
respond TST_CustomerService.get(id)
}
def create() {
respond new TST_Customer(params)
}
def save(TST_Customer TST_Customer) {
if (TST_Customer == null) {
notFound()
return
}
try {
TST_CustomerService.save(TST_Customer)
} catch (ValidationException e) {
respond TST_Customer.errors, view:'create'
return
}
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [message(code: 'TST_Customer.label', default: 'TST_Customer'), TST_Customer.id])
redirect TST_Customer
}
'*' { respond TST_Customer, [status: CREATED] }
}
}
def edit(Long id) {
respond TST_CustomerService.get(id)
}
def update(TST_Customer TST_Customer) {
if (TST_Customer == null) {
notFound()
return
}
try {
TST_CustomerService.save(TST_Customer)
} catch (ValidationException e) {
respond TST_Customer.errors, view:'edit'
return
}
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.updated.message', args: [message(code: 'TST_Customer.label', default: 'TST_Customer'), TST_Customer.id])
redirect TST_Customer
}
'*'{ respond TST_Customer, [status: OK] }
}
}
def delete(Long id) {
if (id == null) {
notFound()
return
}
TST_CustomerService.delete(id)
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.deleted.message', args: [message(code: 'TST_Customer.label', default: 'TST_Customer'), id])
redirect action:"index", method:"GET"
}
'*'{ render status: NO_CONTENT }
}
}
protected void notFound() {
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'TST_Customer.label', default: 'TST_Customer'), params.id])
redirect action: "index", method: "GET"
}
'*'{ render status: NOT_FOUND }
}
}
}
Обслуживание
package TSTSupport
import grails.gorm.services.Service
@Service(TST_Customer)
interface TST_CustomerService {
TST_Customer get(Serializable id)
List<TST_Customer> list(Map args)
Long count()
void delete(Serializable id)
TST_Customer save(TST_Customer TST_Customer)
}
Комментарии:
1. Я видел это в приложении, над которым я работаю, и, похоже, если вы отправляете класс домена в представление, и в этом представлении вы пытаетесь получить доступ к полю, представляющему связь, вы получите эту ошибку. В моем случае это работает, я выделил поле отношения в контроллере и передал его на карте для представления. Я не уверен, есть ли другой способ обойти.
2. @virtualdogbert У меня нет связей для этого класса домена. Фактически, это единственный класс домена в моем приложении. В этой таблице базы данных также нет внешних ключей.
3. Что ж, попробовать стоило, я бы разместил ссылку на ваш вопрос на канале Grails slack. grails. slack.com/messages/C07M0GTDE
4. @virtualdogbert Извините, но я не уверен, как получить реальное приглашение на канал. Я не вижу, где отображается контактная информация администратора.
Ответ №1:
На самом деле мне пришлось искать ответ в Google (ни одно из предоставленных мне предложений не сработало для меня). В любом случае, в контроллере вы добавляете префикс прямо над classname
@Transactional("name of datasource")
Полный пример:
package TSTSupport
import grails.validation.ValidationException
import static org.springframework.http.HttpStatus.*
@Transactional("tst")
class TST_CustomerController {
TST_CustomerService TST_CustomerService
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond TST_CustomerService.list(params), model:[TST_CustomerCount: TST_CustomerService.count()]
}
def show(Long id) {
respond TST_CustomerService.get(id)
}
def create() {
respond new TST_Customer(params)
}
def save(TST_Customer TST_Customer) {
if (TST_Customer == null) {
notFound()
return
}
try {
TST_CustomerService.save(TST_Customer)
} catch (ValidationException e) {
respond TST_Customer.errors, view:'create'
return
}
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [message(code: 'TST_Customer.label', default: 'TST_Customer'), TST_Customer.id])
redirect TST_Customer
}
'*' { respond TST_Customer, [status: CREATED] }
}
}
def edit(Long id) {
respond TST_CustomerService.get(id)
}
def update(TST_Customer TST_Customer) {
if (TST_Customer == null) {
notFound()
return
}
try {
TST_CustomerService.save(TST_Customer)
} catch (ValidationException e) {
respond TST_Customer.errors, view:'edit'
return
}
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.updated.message', args: [message(code: 'TST_Customer.label', default: 'TST_Customer'), TST_Customer.id])
redirect TST_Customer
}
'*'{ respond TST_Customer, [status: OK] }
}
}
def delete(Long id) {
if (id == null) {
notFound()
return
}
TST_CustomerService.delete(id)
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.deleted.message', args: [message(code: 'TST_Customer.label', default: 'TST_Customer'), id])
redirect action:"index", method:"GET"
}
'*'{ render status: NO_CONTENT }
}
}
protected void notFound() {
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'TST_Customer.label', default: 'TST_Customer'), params.id])
redirect action: "index", method: "GET"
}
'*'{ render status: NOT_FOUND }
}
}
}
Комментарии:
1. Удивительно, у меня это работает в Grails 4 , спасибо