java 从数据库加载 FreeMarker 模板
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/357370/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Load FreeMarker templates from database
提问by Dónal
I would like to store my FreeMarker templates in a database table that looks something like:
我想将我的 FreeMarker 模板存储在一个类似于以下内容的数据库表中:
template_name | template_content
---------------------------------
hello |Hello ${user}
goodbye |So long ${user}
When a request is received for a template with a particular name, this should cause a query to be executed, which loads the relevant template content. This template content, together with the data model (the value of the 'user' variable in the examples above), should then be passed to FreeMarker.
当收到对具有特定名称的模板的请求时,这将导致执行查询,该查询加载相关的模板内容。然后,应将此模板内容与数据模型(上面示例中的“用户”变量的值)一起传递给 FreeMarker。
However, the FreeMarker APIseems to assume that each template name corresponds to a file of the same name within a particular directory of the filesystem. Is there any way I can easily have my templates loaded from the DB instead of the filesystem?
但是,FreeMarker API似乎假设每个模板名称都对应于文件系统特定目录中的同名文件。有什么方法可以轻松地从数据库而不是文件系统加载我的模板?
EDIT:I should have mentioned that I would like to be able to add templates to the database while the application is running, so I can't simply load all templates at startup into a new StringTemplateLoader (as suggested below).
编辑:我应该提到我希望能够在应用程序运行时将模板添加到数据库中,所以我不能简单地在启动时将所有模板加载到新的 StringTemplateLoader 中(如下所示)。
回答by Ulf Lindback
We use a StringTemplateLoader to load our tempates which we got from the db (as Dan Vinton suggested)
我们使用 StringTemplateLoader 加载我们从数据库中获得的模板(如 Dan Vinton 建议的那样)
Here is an example:
下面是一个例子:
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);
EditYou don't have to load all templates at startup. Whenever we will access the template, we'll fetch it from the DB and load it through the StringLoader and by calling template.process() we generate (in our case) the XML output.
编辑您不必在启动时加载所有模板。每当我们访问模板时,我们都会从数据库中获取它并通过 StringLoader 加载它,并通过调用 template.process() 生成(在我们的例子中)XML 输出。
回答by Dan Vinton
A couple of ways:
几种方式:
Create a new implementation of TemplateLoaderto load templates direct from the database, and pass it to your Configurationinstance using
setTemplateLoader()prior to loading any templates.Use a StringTemplateLoaderthat you configure from your database when your application starts. Add it to the configuration as above.
创建TemplateLoader的新实现以直接从数据库加载模板,并在加载任何模板之前将其传递给您的Configuration实例
setTemplateLoader()。使用您在应用程序启动时从数据库配置的StringTemplateLoader。将其添加到上述配置中。
Editin light of the questioner's edit, your own implementation of TemplateLoader looks like the way to go. Check the Javadoc here, it's a simple little interface with only four methods, and its behaviour is well documented.
编辑在提问的编辑的光,自己的实现TemplateLoader的样子要走的路。在这里查看 Javadoc ,它是一个简单的小接口,只有四个方法,并且它的行为有据可查。
回答by Jasper de Vries
Since 2.3.20 you can simply construct a Templateusing a string:
从 2.3.20 开始,您可以简单地使用字符串构造 aTemplate:
public Template(String name,
String sourceCode,
Configuration cfg)
throws IOException
which is a convenience constructor for Template(name, new StringReader(sourceCode), cfg).
这是一个方便的构造函数Template(name, new StringReader(sourceCode), cfg)。
回答by Andres
For those looking for some code, here it is. Take a look at the comments in the code for a better understanding.
对于那些正在寻找一些代码的人,这里是。查看代码中的注释以更好地理解。
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 implementation (EMF is an instance of an EntityManagerFactory):
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...
}
}
Setup the configuration class:
设置配置类:
...
TemplateLoaderImpl loader = new TemplateLoaderImpl();
templateConfig = new Configuration(Configuration.VERSION_2_3_25);
templateConfig.setTemplateLoader(loader);
...
And finally, use it:
最后,使用它:
...
long someId = 3L;
Template template = templateConfig.getTemplate("" + someId);
...
This works great, and allows you to use all of Freemarker's features like imports, includes, etc. Look at the following examples:
这很好用,并允许您使用 Freemarker 的所有功能,如导入、包含等。请看以下示例:
<#import "1" as layout> <!-- Use a template id. -->
<@layout.mainLayout>
...
Or in:
或在:
<#include "3"> <!-- Use a template id. -->
...
I use this loader on my own CMS (CinnamonFramework) and works like a charm.
我在我自己的 CMS (CinnamonFramework) 上使用了这个加载器,它的工作原理非常棒。
Best,
最好的,
回答by Lucas Basquerotto
Old question, but for anyone having the same issue, I achieved an easy solution without the need of a custom template loader or having to load the template at startup.
老问题,但对于任何有同样问题的人,我实现了一个简单的解决方案,无需自定义模板加载器,也无需在启动时加载模板。
Suppose you have in your database the dynamic template:
假设您的数据库中有动态模板:
database:
数据库:
<p>Hello <b>${params.user}</b>!</p>
You can just create a Freemarker file (ftlh) that interprets a string received (content) and generates a template from it, using interpret:
您可以创建一个 Freemarker 文件 (ftlh) 来解释接收到的字符串 ( content) 并使用interpret从中生成模板:
dynamic.ftlh:
动态.ftlh:
<#assign inlineTemplate = content?interpret>
<@inlineTemplate />
Then in your java code you only need to get the string from your database (just like retrieving any other data from the database), and use the file that has interpretto generate the template:
然后在您的 java 代码中,您只需要从数据库中获取字符串(就像从数据库中检索任何其他数据一样),并使用interpret必须生成模板的文件:
java:
爪哇:
String content = getFromDatabase();
Configuration cfg = getConfiguration();
String filePath = "dynamic.ftlh";
Map<String, Object> params = new HashMap<String, Object>();
params.put("user", "World");
Map<String, Object> root = new HashMap<>();
root.put("content", content);
root.put("params", params);
Template template = cfg.getTemplate(filePath);
try (Writer out = new StringWriter()) {
template.process(root, out);
String result = out.toString();
System.out.println(result);
}
(Change the methods getFromDatabase()and getConfiguration()to whatever you want to get the dynamic content from the database and get the Freemarker configuration object, respectively)
(分别更改方法getFromDatabase()和getConfiguration()从数据库中获取动态内容并获取Freemarker 配置对象的任何内容)
This should print:
这应该打印:
<p>Hello <b>World</b>!</p>
Then you can change your dynamic content in the database or create others, add new parameters and so on, without the need of creating other Freemarker files (ftlh).
然后您可以更改数据库中的动态内容或创建其他内容、添加新参数等,而无需创建其他 Freemarker 文件 (ftlh)。
回答by Krystian Fiertek
Implement configuration.
实施配置。
Example :
例子 :
@Configuraton
public class FreemarkerConfig {
@Autowired
TemplateRepository tempRepo;
@Autowired
TemplateUtils tempUtils;
@Primary
@Bean
public FreeMarkerConfigurationFactoryBean getFreeMarkerConfiguration() {
// Create new configuration bean
FreeMarkerConfigurationFactoryBean bean = new FreeMarkerConfigurationFactoryBean();
// Create template loader
StringTemplateLoader sTempLoader = new StringTemplateLoader();
// Find all templates
Iterable<TemplateDb> ite = tempRepo.findAll();
ite.forEach((template) -> {
// Put them in loader
sTempLoader.putTemplate(template.getFilename(), template.getContent());
});
// Set loader
bean.setPreTemplateLoaders(sTempLoader);
return bean;
}
}
}
Then u can use it like this :
然后你可以像这样使用它:
@Autowired
private Configuration freemarkerConfig;
Template template = freemarkerConfig.getTemplate(templateFilePath);
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, mapTemplate);

