Me gustaría tienda de mi FreeMarker plantillas en una tabla de base de datos que se ve algo como:

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

Cuando se recibe una solicitud para una plantilla con un nombre en particular, esto debe provocar una consulta que se ejecuta, en el que se carga el correspondiente contenido de la plantilla. Este contenido de la plantilla, junto con el modelo de datos (el valor de la ‘usuario’ variable en los ejemplos anteriores), debe ser transmitida a FreeMarker.

Sin embargo, la FreeMarker API parece asumir que cada nombre de la plantilla corresponde a un archivo con el mismo nombre dentro de un determinado directorio del sistema de archivos. Es allí cualquier manera de la que puedo tener fácilmente mis plantillas de carga de la base de datos en lugar del sistema de archivos?

EDICIÓN: debería haber mencionado que me gustaría ser capaz de añadir plantillas de la base de datos mientras se ejecuta la aplicación, así que simplemente no me puedo cargar todas las plantillas al inicio en una nueva StringTemplateLoader (como se sugiere a continuación).

InformationsquelleAutor Dónal | 2008-12-10

6 Comentarios

  1. 28

    Utilizamos un StringTemplateLoader para cargar nuestras plantillas que tenemos de la base de datos (como Dan Vinton sugerido)

    Aquí está un ejemplo:

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

    Editar
    Usted no tiene que cargar todas las plantillas en el inicio. Siempre vamos a tener acceso a la plantilla, vamos a recuperar de la base de datos y de carga a través de la StringLoader y llamando a la plantilla.proceso() vamos a generar (en nuestro caso) el XML de salida.

  2. 18

    Un par de formas:

    • Crear una nueva aplicación de TemplateLoader para cargar plantillas directamente desde la base de datos, y se pasa a su Configuración instancia mediante setTemplateLoader() antes de la carga de las plantillas.

    • Utilizar un StringTemplateLoader que se configuran a partir de la base de datos cuando se inicia la aplicación. Agregar a la configuración anterior.

    Editar a la luz de la interrogador de la edición, su propia implementación de TemplateLoader parece el camino a seguir. Compruebe el Javadoc aquí, es un poco simple interfaz con sólo cuatro métodos, y que su comportamiento está bien documentado.

  3. 3

    Para aquellos que buscan un poco de código, aquí está. Echa un vistazo a los comentarios en el código para una mejor comprensión.

    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 aplicación (EMF es una instancia de 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...
        }
    
    }

    La configuración de la clase:

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

    Y, por último, utilice:

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

    Esto funciona muy bien, y permite el uso de Freemarker características como las importaciones, incluye, etc. Observe los siguientes ejemplos:

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

    O en:

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

    Yo uso este loader en mi propio CMS (CinnamonFramework) y funciona como un encanto.

    Mejor,

    • Creo que se puede establecer la Configuración.setLocalizedLookup(boolean) para deshabilitar localizada de búsqueda para que usted no tiene que coger NumberFormatException.
  4. 1

    Vieja pregunta, pero para alguien con el mismo problema, he conseguido una solución fácil sin la necesidad de una plantilla personalizada cargador o tener que cargar la plantilla en el inicio.

    Supongamos que usted tiene en su base de datos de la dinámica de la plantilla:

    de la base de datos:

    <p>Hello <b>${params.user}</b>!</p>

    Usted puede crear una Freemarker archivo (ftlh) que interpreta una cadena recibido (content) y genera una plantilla a partir de ella, mediante interpretar:

    dinámico.ftlh:

    <#assign inlineTemplate = content?interpret>
    <@inlineTemplate />

    A continuación, en el código java se necesita la cadena de su base de datos (como la recuperación de cualquier otro tipo de datos de la base de datos), y utilizar el archivo que ha interpret para generar la plantilla:

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

    (Cambiar los métodos getFromDatabase() y getConfiguration() a lo que usted desea obtener el contenido dinámico de la base de datos y obtener la Freemarker objeto de configuración, respectivamente)

    Este debe de impresión:

    <p>Hello <b>World</b>!</p>

    Entonces usted puede cambiar su contenido dinámico en la base de datos o crear otros, añadir nuevos parámetros y así sucesivamente, sin la necesidad de crear otros Freemarker archivos (ftlh).

  5. 0

    Aplicar la configuración.

    Ejemplo :

    @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;
    }

    }

    Entonces u puede utilizar como esto :

    @Autowired
    private Configuration freemarkerConfig;
    
      Template template = freemarkerConfig.getTemplate(templateFilePath);
      String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, mapTemplate);

Dejar respuesta

Please enter your comment!
Please enter your name here