2008-12-10 8 views
19

Je voudrais stocker mes modèles de FreeMarker dans une table de base de données qui ressemble à quelque chose comme:modèles FreeMarker Charger la base de données

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

Lorsqu'une demande est reçue pour un modèle avec un nom particulier, cela devrait provoquer une requête à exécuter, qui charge le contenu du modèle pertinent. Ce contenu de modèle, avec le modèle de données (la valeur de la variable 'user' dans les exemples ci-dessus), devrait ensuite être transmis à FreeMarker.

Cependant, le FreeMarker API semble supposer que chaque nom de modèle correspond à un fichier du même nom dans un répertoire particulier du système de fichiers. Est-ce que je peux facilement charger mes modèles à partir de la base de données au lieu du système de fichiers?

EDIT: Je l'ai dit que je voudrais être en mesure d'ajouter des modèles à la base de données alors que l'application est en cours d'exécution, donc je ne peux pas simplement charger tous les modèles au démarrage dans une nouvelle StringTemplateLoader (comme suggéré ci-dessous).

Cheers, Don

Répondre

18

deux façons:

  • Créer une nouvelle implémentation de TemplateLoader pour charger des modèles directement à partir de la base de données, et le transmettre à votre Configuration instance à l'aide setTemplateLoader() avant le chargement tous les modèles.

  • Utilisez un StringTemplateLoader que vous configurez à partir de votre base de données lorsque votre application démarre. Ajoutez-le à la configuration comme ci-dessus.

Modifier à la lumière de modifier le questionneur, votre propre implémentation de TemplateLoader ressemble le chemin à parcourir. Vérifiez le Javadoc here, c'est une petite interface simple avec seulement quatre méthodes, et son comportement est bien documenté.

26

Nous utilisons un StringTemplateLoader pour charger nos tempates que nous avons obtenu du db (comme Dan Vinton suggéré)

Voici un exemple:

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); 

Modifier Vous n'avez pas charger tous les modèles au démarrage. Chaque fois que nous accèderons au template, nous le récupérerons dans la base de données et le chargerons dans le StringLoader. En appelant template.process(), nous générons (dans notre cas) la sortie XML.

1

Pour ceux qui recherchent du code, le voici. Jetez un oeil aux commentaires dans le code pour une meilleure compréhension.

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; 

} 

mise en œuvre de TemplateLoader (FEM est une instance d'un 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... 
    } 

} 

Configuration de la classe de configuration:

... 
TemplateLoaderImpl loader = new TemplateLoaderImpl(); 

templateConfig = new Configuration(Configuration.VERSION_2_3_25); 
templateConfig.setTemplateLoader(loader); 
... 

Et enfin, l'utiliser:

... 
long someId = 3L; 
Template template = templateConfig.getTemplate("" + someId); 
... 

Cela fonctionne très bien, et vous permet d'utiliser toutes les fonctionnalités de Freemarker comme les importations, comprend, etc. Regardez les exemples suivants:

<#import "1" as layout> <!-- Use a template id. --> 
<@layout.mainLayout> 
... 

Ou dans:

<#include "3"> <!-- Use a template id. --> 
... 

J'utilise ce chargeur sur mon propre CMS (CinnamonFramework) et fonctionne comme un charme.

Best,

Questions connexes