Groovy 将动态查询结果写入文件

Groovy 将动态查询结果写入文件,groovy,Groovy,我正在尝试用Groovy编写一个通用程序,该程序将从配置文件中获取SQL以及其他参数,并将它们放入配置文件中 节目如下: def config = new ConfigSlurper().parse(new File("config.properties").toURL()) Sql sql = Sql.newInstance(config.db.url, config.db.login, config.db.password, config.db.driver); def

我正在尝试用Groovy编写一个通用程序,该程序将从配置文件中获取SQL以及其他参数,并将它们放入配置文件中

节目如下:

def config = new ConfigSlurper().parse(new File("config.properties").toURL())
Sql sql = Sql.newInstance(config.db.url, config.db.login, config.db.password, config.db.driver);

def fileToWrite = new File(config.copy.location)
def writer = fileToWrite.newWriter()
writer.write(config.file.headers)

sql.eachRow(config.sql){ res->
    writer.write(config.file.rows)
}
在配置中,sql如下所示:

sql=“从mydb中选择*”

file.rows=“${res.column1}|${res.column2}|${res.column3}\n”

当我运行它时,我得到

[:]|[:]|[:]

[:]|[:]|[:]

[:]|[:]|[:]

在文件中。如果我替换

 writer.write(config.file.rows)


它输出实际结果。要获得结果,我需要做哪些不同的操作?

好消息是,
ConfigSlurper
能够按预期为您执行
GString
变量替换。坏消息是,它在调用上面的
parse()
方法时会进行这种替换,而在您将
res
变量替换到解析器之前很久。另一个坏消息是,如果被替换的变量没有在配置文件本身中定义,那么您必须通过
绑定
属性提前将它们提供给slurper

因此,要获得您想要的效果,您必须通过每一次
来解析属性。这是否意味着您必须创建一个新的
ConfigSlurper
每行重新读取一次文件?不可以。您必须为每次传递创建一个新的
ConfigObject
,但您可以重用
ConfigSlurper
和文件文本,如下所示:

def slurper = new ConfigSlurper();
def configText = new File("scripts/config.properties").text
def config = slurper.parse(configText)
Sql sql = Sql.newInstance(config.db.url, config.db.login, config.db.password, config.db.driver);

def fileToWrite = new File(config.copy.location)
def writer = fileToWrite.newWriter()
writer.write(config.file.headers)

sql.eachRow(config.sql){ result ->
    slurper.binding = [res:result]
    def reconfig = slurper.parse(configText)
    print(reconfig.file.rows)
}
请注意,我将闭包参数的名称从
res
更改为
result
。我这样做是为了强调slurper是从绑定映射键而不是从闭包参数名称中绘制名称
res


如果希望减少浪费的“重新分析”时间和精力,可以将
file.rows
属性分离到它自己的单独文件中。我仍然会读入该文件文本一次,并在“每行”解析中重用该文本。

好消息是,
ConfigSlurper
完全能够按预期为您执行
GString
变量替换。坏消息是,它在调用上面的
parse()
方法时会进行这种替换,而在您将
res
变量替换到解析器之前很久。另一个坏消息是,如果被替换的变量没有在配置文件本身中定义,那么您必须通过
绑定
属性提前将它们提供给slurper

因此,要获得您想要的效果,您必须通过每一次
来解析属性。这是否意味着您必须创建一个新的
ConfigSlurper
每行重新读取一次文件?不可以。您必须为每次传递创建一个新的
ConfigObject
,但您可以重用
ConfigSlurper
和文件文本,如下所示:

def slurper = new ConfigSlurper();
def configText = new File("scripts/config.properties").text
def config = slurper.parse(configText)
Sql sql = Sql.newInstance(config.db.url, config.db.login, config.db.password, config.db.driver);

def fileToWrite = new File(config.copy.location)
def writer = fileToWrite.newWriter()
writer.write(config.file.headers)

sql.eachRow(config.sql){ result ->
    slurper.binding = [res:result]
    def reconfig = slurper.parse(configText)
    print(reconfig.file.rows)
}
请注意,我将闭包参数的名称从
res
更改为
result
。我这样做是为了强调slurper是从绑定映射键而不是从闭包参数名称中绘制名称
res


如果希望减少浪费的“重新分析”时间和精力,可以将
file.rows
属性分离到它自己的单独文件中。我仍然会读入该文件文本一次,并在“每行”解析中重用该文本。

您可以通过使用Gstring的延迟求值和更改委托来实现这一点。 首先,通过使值成为调用闭包的结果,使Gstring变为惰性:

file.rows="${->res.column1}|${->res.column2}|${-> res.column3}"
然后,在评估更改闭包的委托之前:

config.file.rows.values.each {
    if (Closure.class.isAssignableFrom(it.getClass())) {
       it.resolveStrategy = Closure.DELEGATE_FIRST
       it.delegate = this
    }
}
委托必须在作用域中包含变量res。下面是一个完整的工作示例:

class Test {
Map res

void run() {
    String configText = '''file.rows="${->res.column1}|${->res.column2}|${-> res.column3}"
    sql="select * from mydb"'''
    def slurper = new ConfigSlurper()
    def config = slurper.parse(configText)

    config.file.rows.values.each {
        if (Closure.class.isAssignableFrom(it.getClass())) {
           it.resolveStrategy = Closure.DELEGATE_FIRST
           it.delegate = this
        }
    }

    def results = [
            [column1: 1, column2: 2, column3: 3],
            [column1: 4, column2: 5, column3: 6],
        ]

    results.each {
        res = it
        println config.file.rows.toString()
    }
}
}
new Test().run()

您可以通过使用Gstring的延迟求值和修改委托来实现这一点。 首先,通过使值成为调用闭包的结果,使Gstring变为惰性:

file.rows="${->res.column1}|${->res.column2}|${-> res.column3}"
然后,在评估更改闭包的委托之前:

config.file.rows.values.each {
    if (Closure.class.isAssignableFrom(it.getClass())) {
       it.resolveStrategy = Closure.DELEGATE_FIRST
       it.delegate = this
    }
}
委托必须在作用域中包含变量res。下面是一个完整的工作示例:

class Test {
Map res

void run() {
    String configText = '''file.rows="${->res.column1}|${->res.column2}|${-> res.column3}"
    sql="select * from mydb"'''
    def slurper = new ConfigSlurper()
    def config = slurper.parse(configText)

    config.file.rows.values.each {
        if (Closure.class.isAssignableFrom(it.getClass())) {
           it.resolveStrategy = Closure.DELEGATE_FIRST
           it.delegate = this
        }
    }

    def results = [
            [column1: 1, column2: 2, column3: 3],
            [column1: 4, column2: 5, column3: 6],
        ]

    results.each {
        res = it
        println config.file.rows.toString()
    }
}
}
new Test().run()

我尝试了这一点,但它的内存不足:java.lang.OutOfMemoryError:java堆空间。在闭包末尾添加reconfig=null没有帮助。它为什么要使用内存,不是应该为每一行释放内存吗?无法保证垃圾收集何时(或是否)运行。尝试在
sql.eachRow
if(!(i%100)System.gc())
之前使用
int i=1
作为闭包中的第一条语句。话虽如此,您是否尝试过@Dylan Bijnagte的解决方案?它看起来非常有效,尤其是与我的相比。我确实尝试过,但得到了相同的结果-[:]|[:]|[:]我尝试过这个方法,但它的内存不足,错误为:java.lang.OutOfMemoryError:java堆空间。在闭包末尾添加reconfig=null没有帮助。它为什么要使用内存,不是应该为每一行释放内存吗?无法保证垃圾收集何时(或是否)运行。尝试在
sql.eachRow
if(!(i%100)System.gc())
之前使用
int i=1
作为闭包中的第一条语句。话虽如此,您是否尝试过@Dylan Bijnagte的解决方案?它看起来相当有效,尤其是与我的相比。我确实尝试过,但我得到了相同的结果-[:]|[:]|[:]我今天学到了一些新东西。当这种情况发生时,总是很好的一天。我尝试了它,但仍然得到相同的结果-[:]|[:]|[:]当你检查
config.files.rows
的类时,它是什么
config.files.rows.getClass()
应该是一个GString。当
res
也在范围内时,必须调用
config.file.rows.toString()
。它是gtoovy.util。ConfigObject@BalRog必须将其指定为
config.file.rows.getClass()
,以避免映射返回
'class'
键的值。我今天学到了一些新东西。当这种情况发生时,总是很好的一天。我尝试过,但仍然得到相同的结果-[:]|[:]|[:]当你检查
config的类时。