负载的FreeMarker模板从数据库
我想我的FreeMarker模板存储在数据库中的表,看起来像:负载的FreeMarker模板从数据库
template_name | template_content
---------------------------------
hello |Hello ${user}
goodbye |So long ${user}
当接收到一个请求模板具有特定名称,这一点应引起查询被执行,加载相关的模板内容。然后应将此模板内容与数据模型(上述示例中的“用户”变量的值)一起传递给FreeMarker。
但是,FreeMarker API似乎假定每个模板名称对应于文件系统的特定目录中的同名文件。有什么办法可以轻松地从数据库而不是文件系统加载我的模板?
编辑:我应该提到,我希望能够在应用程序运行时模板添加到数据库中,所以我不能在启动时的所有模板只需加载到一个新的StringTemplateLoader(如下建议)。
干杯, 唐
几种方法:
创建一个新的执行TemplateLoader加载模板从数据库中直接,并将它传递给使用
setTemplateLoader()
,在装船前你Configuration实例任何模板。使用您在应用程序启动时从数据库配置的StringTemplateLoader。将它添加到上面的配置中。
编辑在提问的编辑的光,自己的实现TemplateLoader的样子要走的路。检查Javadoc here,这是一个简单的小接口,只有四种方法,其行为已被充分记录。
我们使用StringTemplateLoader加载我们tempates这是我们从数据库中得到(丹文顿建议)
下面是一个例子:
StringTemplateLoader stringLoader = new StringTemplateLoader();
String firstTemplate = "firstTemplate";
stringLoader.putTemplate(firstTemplate, freemarkerTemplate);
// It's possible to add more than one template (they might include each other)
// String secondTemplate = "<#include \"greetTemplate\"><@greet/> World!";
// stringLoader.putTemplate("greetTemplate", secondTemplate);
Configuration cfg = new Configuration();
cfg.setTemplateLoader(stringLoader);
Template template = cfg.getTemplate(firstTemplate);
编辑 您不必在启动时加载所有模板。每当我们访问模板时,我们都会从数据库中获取数据,并通过StringLoader加载并通过调用template.process()来生成(在我们的例子中)XML输出。
由于2.3.20,你可以简单地construct a Template
using a string:
public Template(String name,
String sourceCode,
Configuration cfg)
throws IOException
对于那些寻找一些代码,在这里。查看代码中的注释以更好地理解。
DBTemplate:
@Entity
public class DBTemplate implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private long templateId;
private String content; // Here's where the we store the template
private LocalDateTime modifiedOn;
}
TemplateLoader实现(EMF是一个EntityManagerFactory的实例):
public class TemplateLoaderImpl implements TemplateLoader {
public TemplateLoaderImpl() { }
/**
* Retrieves the associated template for a given id.
*
* When Freemarker calls this function it appends a locale
* trying to find a specific version of a file. For example,
* if we need to retrieve the layout with id = 1, then freemarker
* will first try to load layoutId = 1_en_US, followed by 1_en and
* finally layoutId = 1.
* That's the reason why we have to catch NumberFormatException
* even if it is comes from a numeric field in the database.
*
* @param layoutId
* @return a template instance or null if not found.
* @throws IOException if a severe error happens, like not being
* able to access the database.
*/
@Override
public Object findTemplateSource(String templateId) throws IOException {
EntityManager em = null;
try {
long id = Long.parseLong(templateId);
em = EMF.getInstance().getEntityManager();
DBTemplateService service = new DBTemplateService(em);
Optional<DBTemplate> result = service.find(id);
if (result.isPresent()) {
return result.get();
} else {
return null;
}
} catch (NumberFormatException e) {
return null;
} catch (Exception e) {
throw new IOException(e);
} finally {
if (em != null && em.isOpen()) {
em.close();
}
}
}
/**
* Returns the last modification date of a given template.
* If the item does not exist any more in the database, this
* method will return Long's MAX_VALUE to avoid freemarker's
* from recompiling the one in its cache.
*
* @param templateSource
* @return
*/
@Override
public long getLastModified(Object templateSource) {
EntityManager em = null;
try {
em = EMF.getInstance().getEntityManager();
DBTemplateService service = new DBTemplateService(em);
// Optimize to only retrieve the date
Optional<DBTemplate> result = service.find(((DBTemplate) templateSource).getTemplateId());
if (result.isPresent()) {
return result.get().getModifiedOn().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
} else {
return Long.MAX_VALUE;
}
} finally {
if (em != null && em.isOpen()) {
em.close();
}
}
}
/**
* Returns a Reader from a template living in Freemarker's cache.
*/
@Override
public Reader getReader(Object templateSource, String encoding) throws IOException {
return new StringReader(((DBTemplate) templateSource).getContent());
}
@Override
public void closeTemplateSource(Object templateSource) throws IOException {
// Nothing to do here...
}
}
安装配置类:
...
TemplateLoaderImpl loader = new TemplateLoaderImpl();
templateConfig = new Configuration(Configuration.VERSION_2_3_25);
templateConfig.setTemplateLoader(loader);
...
最后,使用它:
...
long someId = 3L;
Template template = templateConfig.getTemplate("" + someId);
...
这个伟大的工程,并允许您使用所有的Freemarker的功能,如进口,包括等请看下面的例子:
<#import "1" as layout> <!-- Use a template id. -->
<@layout.mainLayout>
...
或者在:
<#include "3"> <!-- Use a template id. -->
...
我用这个加载器在我自己的CMS(CinnamonFramework)上工作就像一个魅力。
最好,
非常好,谢谢! – 2017-02-03 11:20:32