我有一个Grails作业类(grails-app/jobs
)需要调用静态(辅助)方法(在src/groovy
中定义)。此方法分别在2个不同的域对象上调用get-和find-methods。方法调用的结果是返回一个简单的String
(为此可以返回任何东西 - 无关紧要)。
我的问题是,当我调用一个包含2个(可能更多)不同域类的fetch的静态方法时,如何在作业类中使用.withTransaction
或.withSession
? / p>
或者,如何在作业类中声明/使用Hibernate会话,以便我不必使用.withBlaBla?
编辑(底部的另一个编辑 - 抱歉):
获取EZTable和EZRow的行正在工作。 EmailReminder我不得不用EmailReminder.with包装......现在调用ServiceUtils.handleSubjectOrMessageString(ezTable, ezRow, emailReminder.subject)
的行导致异常(这是“现在”添加 - 整个作业类在早期使用简单的String值工作)。
class EmailReminderJob implements Job {
EmailReminder emailReminder
EZTable ezTable
EZRow ezRow
static triggers = {}
def void execute(JobExecutionContext context) {
List<String> emails = new ArrayList<String>(0)
ezTable = EZTable.get(new Long(context.mergedJobDataMap.get('ezTableId')))
ezRow = EZRow.get(new Long(context.mergedJobDataMap.get('ezRowId')))
EmailReminder.withTransaction { status ->
emailReminder = EmailReminder.get(new Long(context.mergedJobDataMap.get('emailReminderId')))
if(emailReminder.sendMessageToOwnerUser && emailReminder.ownerUser.email!=null)
emails.add(emailReminder.ownerUser.email)
if(emailReminder.sendMessageToOwnerCompany && emailReminder.ownerCompany.email!=null)
emails.add(emailReminder.ownerCompany.email)
if(emailReminder.emails!=null && emails.size()>0)
emails.addAll(new ArrayList<String>(emailReminder.emails))
if(emailReminder.messageReceiverUsers!=null && emailReminder.messageReceiverUsers.size()>0) {
for(user in emailReminder.messageReceiverUsers) {
if(user.email!=null)
emails.add(user.email)
}
}
}
if(emails.size()>0) {
String host = "localhost";
Properties properties = System.getProperties();
properties.setProperty("mail.smtp.host", host);
Session session = Session.getDefaultInstance(properties);
try{
// Create a default MimeMessage object.
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(emailReminder.emailFrom));
for(email in emails) {
message.addRecipient(
Message.RecipientType.TO,
new InternetAddress(email)
);
}
message.setSubject(ServiceUtils.handleSubjectOrMessageString(ezTable, ezRow, emailReminder.subject));
message.setText(ServiceUtils.handleSubjectOrMessageString(ezTable, ezRow, emailReminder.definedMessage));
Transport.send(message);
}catch (MessagingException mex) {
mex.printStackTrace();
}
}
}
}
src/groove
下的我的util-class中的静态方法(行EZColumn ezcolumn = EZColumn.get(id)
和下一行导致异常):
def static String handleSubjectOrMessageString(EZTable eztable, EZRow ezrow, String subjectOrMessage) {
String regex = '(?<=\\$\\$)(.*?)(?=\\$\\$)'
Pattern pattern = Pattern.compile(regex)
Matcher matcher = pattern.matcher(subjectOrMessage)
StringBuffer stringBuffer = new StringBuffer();
while(matcher.find()) {
if(subjectOrMessage.substring(matcher.start(), matcher.end()).contains('#')) {
String stringId = subjectOrMessage.substring(matcher.start(), matcher.end()).split('#')[0]
String name = subjectOrMessage.substring(matcher.start(), matcher.end()).split('#')[1]
try {
Long id = new Long(stringId)
EZColumn ezcolumn = EZColumn.get(id)
EZCell ezcell = EZCell.findByEzTableAndEzRowAndEzColumn(eztable, ezrow, ezcolumn)
matcher.appendReplacement(stringBuffer, fetchCellValues(ezcell, ezcolumn))
} catch(NumberFormatException nfe) {
if(stringId.equals("id")) {
if(name.equals("row"))
matcher.appendReplacement(stringBuffer, ezrow.id.toString())
else if(name.equals("table"))
matcher.appendReplacement(stringBuffer, eztable.id.toString())
else
matcher.appendReplacement(stringBuffer, "???")
}
}
}
}
matcher.appendTail(stringBuffer);
println stringBuffer.toString().replaceAll('\\$', "")
return stringBuffer.toString().replaceAll('\\$', "")
}
例外:
| Error 2015-02-11 10:33:33,954 [quartzScheduler_Worker-1] ERROR core.JobRunShell - Job EmailReminderGroup.ER_3_EZTable_3 threw an unhandled Exception:
Message: null
Line | Method
->> 202 | run in org.quartz.core.JobRunShell
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
^ 573 | run in org.quartz.simpl.SimpleThreadPool$WorkerThread
| Error 2015-02-11 10:33:33,996 [quartzScheduler_Worker-1] ERROR core.ErrorLogger - Job (EmailReminderGroup.ER_3_EZTable_3 threw an exception.
Message: Job threw an unhandled exception.
Line | Method
->> 213 | run in org.quartz.core.JobRunShell
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
^ 573 | run in org.quartz.simpl.SimpleThreadPool$WorkerThread
Caused by NullPointerException: null
->> 202 | run in org.quartz.core.JobRunShell
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
^ 573 | run in org.quartz.simpl.SimpleThreadPool$WorkerThread
| Error 2015-02-11 10:33:34,005 [quartzScheduler_Worker-1] ERROR listeners.ExceptionPrinterJobListener - Exception occurred in job: null
Message: org.quartz.SchedulerException: Job threw an unhandled exception. [See nested exception: java.lang.NullPointerException]
Line | Method
->> 218 | run in org.quartz.core.JobRunShell
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
^ 573 | run in org.quartz.simpl.SimpleThreadPool$WorkerThread
Caused by SchedulerException: Job threw an unhandled exception.
->> 213 | run in org.quartz.core.JobRunShell
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
^ 573 | run in org.quartz.simpl.SimpleThreadPool$WorkerThread
Caused by NullPointerException: null
->> 202 | run in org.quartz.core.JobRunShell
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
^ 573 | run in org.quartz.simpl.SimpleThreadPool$WorkerThread
再次编辑:(:
我在静态方法中有很多嵌套调用(fetchCellValues(ezcell, ezcolumn)
中的matcher.appendReplacement(stringBuffer, fetchCellValues(ezcell, ezcolumn))
- 方法调用更深的值来获取值,实际上我在一次调用时得到了“无会话” - 异常调用其他调用尝试获取另一个域对象):
Message: org.quartz.SchedulerException: Job threw an unhandled exception. [See nested exception: org.hibernate.LazyInitializationException: could not initialize proxy - no Session]
答案 0 :(得分:1)
你可以像在任何地方一样使用它们。两者都独立于他们所要求的阶级; withTransaction
只是在事务中运行包装的代码,加入当前活动的事务(如果有的话),withSession
使当前的Hibernate Session
可用于包装的代码,否则不会任何东西。
您没有说明需要任何理由,因此具体建议并不明确。如果您只是在读取数据,则不需要事务,如果您正在调用域类方法,则不需要访问会话。
我过去提倡的withTransaction
的一个用法(几乎是它的唯一用途,因为它通常被滥用)是在没有活动会话时避免延迟加载异常。在withTransaction
块中包装代码具有创建会话并在块的持续时间内保持打开的副作用,并且允许您使用延迟加载的实例和集合。控制器具有活动会话,因为有一个开放式会话视图拦截器,它在请求开始时启动会话,将其存储在ThreadLocal
中,并在请求结束时刷新并关闭它。作业很相似,因为插件使用Quartz作业开始/结束事件来做同样的事情。
但是,由于延迟加载或者您正在更新,是否要使代码处于事务性状态,您通常应该在事务性服务中进行工作。
服务非常适合事务性工作,因为它们在默认情况下是事务性的(只有没有@Transactional
注释且包含static transactional = false
的服务是非事务性的),并且很容易配置事务划分。具有@Transactional
注释的class和per-method。它们也非常适合封装业务逻辑,而不管它们的调用方式如何;通常不需要服务方法来获得任何HTTP / Job /等。意识,只需将String / number / boolean / object参数中所需的数据传递给它,让它完成它的工作。
我喜欢保持控制器简单,从请求参数进行数据绑定并调用服务来完成实际工作,然后呈现响应或路由到下一页,我在Quartz作业中做同样的事情。使用Quartz作为其调度功能,但在服务中进行实际工作。依赖 - 像任何bean(def fooService
)一样注入服务,并将所有业务逻辑和数据库放在那里。它使代码中的内容保持清晰,并使测试更容易,因为您可以测试服务方法,而无需模拟HTTP调用或Quartz。