java - template - freemarker vs velocity
Cargar plantillas de FreeMarker desde la base de datos (4)
Un par de maneras:
Cree una nueva implementación de TemplateLoader para cargar plantillas directamente desde la base de datos y páselo a su instancia de Configuración usando
setTemplateLoader()
antes de cargar cualquier plantilla.Use un StringTemplateLoader que configure desde su base de datos cuando se inicie su aplicación. Agrégalo a la configuración como se indica arriba.
Edite a la luz de la edición del cuestionario, su propia implementación de TemplateLoader se ve como el camino a seguir. Compruebe el Javadoc aquí , es una pequeña interfaz simple con solo cuatro métodos, y su comportamiento está bien documentado.
Me gustaría almacenar mis plantillas de FreeMarker en una tabla de base de datos que se parece a algo así como:
template_name | template_content
---------------------------------
hello |Hello ${user}
goodbye |So long ${user}
Cuando se recibe una solicitud para una plantilla con un nombre particular, esto debería provocar la ejecución de una consulta, que carga el contenido relevante de la plantilla. Este contenido de la plantilla, junto con el modelo de datos (el valor de la variable ''usuario'' en los ejemplos anteriores), se debe pasar a FreeMarker.
Sin embargo, la API de FreeMarker parece suponer que cada nombre de plantilla corresponde a un archivo del mismo nombre dentro de un directorio particular del sistema de archivos. ¿Hay alguna forma en la que pueda cargar fácilmente mis plantillas desde la base de datos en lugar del sistema de archivos?
EDITAR: Debería haber mencionado que me gustaría poder agregar plantillas a la base de datos mientras la aplicación se está ejecutando, por lo que no puedo simplemente cargar todas las plantillas al inicio en un nuevo StringTemplateLoader (como se sugiere a continuación).
Saludos, Don
Usamos un StringTemplateLoader para cargar nuestros filtros que obtuvimos del DB (como sugirió Dan Vinton)
Aquí hay 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 No es necesario cargar todas las plantillas al inicio. Cada vez que accedemos a la plantilla, la buscamos desde el DB y la cargamos a través del StringLoader y al llamar a template.process () generamos (en nuestro caso) el resultado XML.
Desde 2.3.20 puedes simplemente construir una Template
usando una cadena :
public Template(String name,
String sourceCode,
Configuration cfg)
throws IOException
que es un constructor de conveniencia para Template(name, new StringReader(sourceCode), cfg)
.
Para aquellos que buscan algún código, aquí está. Eche 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;
}
Implementación de TemplateLoader (EMF es una instancia de 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...
}
}
Configure la clase de configuración:
...
TemplateLoaderImpl loader = new TemplateLoaderImpl();
templateConfig = new Configuration(Configuration.VERSION_2_3_25);
templateConfig.setTemplateLoader(loader);
...
Y finalmente, úsalo:
...
long someId = 3L;
Template template = templateConfig.getTemplate("" + someId);
...
Funciona de maravilla y le permite usar todas las características de Freemarker, como importaciones, inclusiones, etc. Mire los siguientes ejemplos:
<#import "1" as layout> <!-- Use a template id. -->
<@layout.mainLayout>
...
O en:
<#include "3"> <!-- Use a template id. -->
...
Uso este cargador en mi propio CMS (CinnamonFramework) y funciona como un encanto.
Mejor,