Mysql 对于Grails,优化与共享数据和访问数据库相关的控制器到服务调用
我有一组域,如下所示:Mysql 对于Grails,优化与共享数据和访问数据库相关的控制器到服务调用,mysql,grails,groovy,Mysql,Grails,Groovy,我有一组域,如下所示: class Unit { String unitName Long latitude Long longitude Template template static belongsTo = [Template] } class Template { String templateName static hasMany = [features: Feature, sy
class Unit {
String unitName
Long latitude
Long longitude
Template template
static belongsTo = [Template]
}
class Template {
String templateName
static hasMany = [features: Feature,
systems: Unit]
}
class Feature {
String featureName
static hasMany = [templates: Template, components: Component]
static belongsTo = [Template]
}
class Component {
String componentName
static hasMany = [fileParts: FilePart,
listDefinitions: ListDefinition,
settings: Setting,
features: Feature]
static belongsTo = [Feature]
}
class Setting {
String settingName
SettingTypeEnum settingTypeEnum
String settingValue
static hasMany = [components: Component]
static belongsTo = [Component]
} // more tables not shown.
创建如下MySQL结构:
在服务方法中,给定单元Id,我将需要的所有相关值收集到映射中
class UnitService {
boolean transactional = false
def serviceGatherAllSystem(Long sysid) {
def sys = Unit.get(sysid)
def builder = [
sys.collect{
[
systemName: sys.unitName,
templateName: sys.template.templateName,
features: sys.template.features.collect{ featureItem ->
[
featureName: featureItem.featureName,
components: featureItem.components.collect{ comp -> [
compName: comp.name,
settings: comp.settings.collect{ sets -> [
settingName: sets.name,
settingType: sets.settingTypeEnum.value,
settingValue: sets.value
]
},//settings
files: comp.fileParts.collect{ files->
[
fileName: files.name,
fileType: files.filePartTypeEnum.value,
fileValue: files.value
]
},//files
listsDef: comp.listDefinitions.collect{ list->
[
listID: list.id,
listName: list.listDefinitionName,
listDisplayName: list.listDisplayName, listLevelCount: list.levelCount
]
}
]
}//components
]
}//features
]
}//sys.collect
]//def builder
return builder
}//serviceGatherSytem
一旦我完成了DB调用并检索了这些值,我想在请求期间使用它们,然后“潜入”结果映射以获得具体细节。我认为我可以在控制器级别通过将其放入控制器来实现这一点
def unitService //the UnitService.groovy class
def unitDetails
def index() {
longId = (params.unitId).toLong()
unitDetails = unitService.serviceGatherAllSystem(longId)
}
然后,在任何时候,我都可以在控制器的其他方法中查询unitDetails,并获得ServiceGathereAllSystem()的结果,而无需再次获取数据库。
然而,我希望将用于从ServiceGathereAllSystem()的结果中提取数据的一系列逻辑和编码推送到UnitService.groovy中,而不是在控制器中对其进行编码以保持控制器精简并在其他地方使用
我的假设是,如果我在UnitServices.groovy中定义了另一个调用ServiceGathereAllSystem()的方法(如下),那么我将导致重新查询数据库,对吗
我是否应该在UnitServices.groovy showDetails()中创建另一个方法,将unitDetails作为参数并从控制器中调用它?这样,我就可以在请求时调用ServiceGathereAllSystem(),并将结果传递给询问者
或者有没有一种Groovy方法可以让我在请求级别保存结果,或者在UnitService.Groovy类中“保存”它们,然后在UnitService.Groovy中的另一个方法中再次使用它们
class UnitService{
def serviceGatherAllSystem(Long sysid) {
...
}
//invoke serviceGatherAllSystem() causes another DB fetch, right???
def serviceJsonSettings(long id){
String jsons = ""
def result = serviceGatherAllSystem(id)
result.features.components.settings.each(){ a ->
a.each() { b ->
b.each() {c ->
c.each(){d ->
d.each(){
if (it.settingsType == SettingTypeEnum.JSON.value){jsons = jsons + it.settingValue + ", "}
}
}
}
}
}
return jsons.substring(0, jsons.length() - 2)
}
//pass results of serviceGatherAllSystem()
def serviceJsonSettings(Map unitDetails){
String jsons = ""
unitDetails.features.components.settings.each(){ a ->
a.each() { b ->
b.each() {c ->
c.each(){d ->
d.each(){
if (it.settingsType == SettingTypeEnum.JSON.value){jsons = jsons + it.settingValue + ", "}
}
}
}
}
}
return jsons.substring(0, jsons.length() - 2)
}
...
}
首先,我不理解unitService.ServiceGathereAllSystem的意义。为什么要费心将所有域对象转换为嵌套映射的层次结构,而您可能只需要导航域类层次结构,例如,替换它
def index() {
longId = (params.unitId).toLong()
unitDetails = unitService.serviceGatherAllSystem(longId)
}
用这个
def index() {
Unit unit = Unit.get(params.unitId)
}
虽然您可以将单元
存储在控制器的一个字段中并从那里访问它(因为每个请求都会创建一个单独的控制器类实例),但我不喜欢这种方法,我自己也从不使用它。我不喜欢这种方法的一个原因是,它会导致难以阅读的代码。例如,考虑这个控制器
class MyController {
private Foo foo
private Bar bar
private Unit unit
def index() {
unit = Unit.get(params.unitId)
foo = Foo.get(params.fooId)
bar = Unit.get(params.barId)
doSomething()
doSomethingElse()
}
private doSomething() {
//implementation omitted
}
private doSomethingElse() {
//implementation omitted
}
}
没有简单的方法可以看出doSomething
和doSomethingElse
正在使用哪些字段foo
、bar
、unit
,而如果您这样编写代码
class MyController {
def index() {
Unit unit = Unit.get(params.unitId)
Foo foo = Foo.get(params.fooId)
Bar bar = Unit.get(params.barId)
doSomething(unit)
doSomethingElse(foo, bar)
}
private doSomething(Unit unit) {
//implementation omitted
}
private doSomethingElse(Foo foo, Bar bar) {
//implementation omitted
}
}
只需查看doSomething
和doSomethingElse
的参数列表,即可查看它们使用的对象。一般来说,如果可能的话,最好避免使用有“副作用”的方法
在上面的示例中,业务逻辑是由控制器的私有方法执行的,但是最好将业务逻辑移到服务方法中。这样做的一个原因是,服务可以在整个应用程序中重复使用(可以从任何控制器、taglib、域类等调用它们),还因为服务在默认情况下是事务性的
更新
继您下面的评论之后,我现在了解到将域对象层次结构转换为嵌套映射是一种尝试,即一次加载所有数据
但是,假设域类之间的关系是惰性的(默认),那么嵌套映射将不会从单个查询中构造—每次访问关联时,都会发出一个单独的查询。打开SQL日志记录以确认这一点。通过一个查询获得所需数据的唯一方法是使域类之间的关联更为密切。您可以使这些关联在每个查询的基础上进行,也可以使它们在所有查询的基础上进行。如果您想要后者,那么您可以在域类本身中配置急切关联。您可能需要考虑使用急切抓取执行HQL查询,或者使用相同的条件查询。一旦这样做,就可以将其归结为一个底层查询。虽然您可以将(分离的)结果存储在服务中并重新服务以避免后续查询,但您可能会发现一个查询可能足够快,或者二级缓存已经提供了您所需的内容。感谢您的输入@Don。如果我抓取单元,然后在组装请求响应时在需要访问相关表的每个位置遍历,那么我是否会导致多次读取数据库?我创建的大型映射是对DB的一次“命中”,然后我可以在稍后组装其余响应时查看映射。我的想法是,这将减少对数据库的点击并提高性能。是哪种情况?