2008-12-10 97 views
19

我想我的FreeMarker模板存儲在數據庫中的表,看起來像:負載的FreeMarker模板從數據庫

template_name | template_content 
--------------------------------- 
hello   |Hello ${user} 
goodbye  |So long ${user} 

當接收到一個請求模板具有特定名稱,這一點應引起查詢被執行,加載相關的模板內容。然後應將此模板內容與數據模型(上述示例中的「用戶」變量的值)一起傳遞給FreeMarker。

但是,FreeMarker API似乎假定每個模板名稱對應於文件系統的特定目錄中的同名文件。有什麼辦法可以輕鬆地從數據庫而不是文件系統加載我的模板?

編輯:我應該提到,我希望能夠在應用程序運行時模板添加到數據庫中,所以我不能在啓動時的所有模板只需加載到一個新的StringTemplateLoader(如下建議)。

乾杯, 唐

回答

18

幾種方法:

  • 創建一個新的執行TemplateLoader加載模板從數據庫中直接,並將它傳遞給使用setTemplateLoader(),在裝船前你Configuration實例任何模板。

  • 使用您在應用程序啓動時從數據庫配置的StringTemplateLoader。將它添加到上面的配置中。

編輯在提問的編輯的光,自己的實現TemplateLoader的樣子要走的路。檢查Javadoc here,這是一個簡單的小接口,只有四種方法,其行爲已被充分記錄。

26

我們使用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輸出。

1

對於那些尋找一些代碼,在這裏。查看代碼中的註釋以更好地理解。

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)上工作就像一個魅力。

最好,