I'm using Spring Boot and Freemarker. I'm loading my template from db using a custom loader:
public class ContentDbLoader implements TemplateLoader {
#Inject
private TemplateRepository templateRepository;
#Override
public void closeTemplateSource(Object templateSource) throws IOException {
return;
}
#Override
public Object findTemplateSource(String name) throws IOException {
return getTemplateFromName(name);
}
#Override
public long getLastModified(Object templateSource) {
MyTemplate template = (MyTemplate) templateSource;
template = templateRepository.findOne(template.getId());
return template.getLastModifiedDate().toEpochMilli();
}
#Override
public Reader getReader(Object templateSource, String encoding) throws IOException {
return new StringReader(((Template) templateSource).getContent());
}
private MyTemplate getTemplateFromName(String name) {
//my logic
}
}
My model is:
Entity
#Table(uniqueConstraints = { #UniqueConstraint(columnNames = { "channel", "communicationType", "country_id" }) })
public class Template extends AbstractEntity {
private static final long serialVersionUID = 8405971325717828643L;
#NotNull
#Column(nullable = false)
#Enumerated(EnumType.STRING)
private Channel channel;
#NotNull
#Column(nullable = false)
#Enumerated(EnumType.STRING)
private CommunicationType communicationType;
#Size(max = 255)
private String subject;
#NotNull
#Column(nullable = false)
#Lob
private String content;
#NotNull
#Column(nullable = false)
private String sender;
#NotNull
#ManyToOne(fetch = FetchType.LAZY, optional = false)
private Country country;
As you can see I return a MyTemplate object coming from db. When I get the custom template to precess the text, I do this:
contentFreemarkerConfig.getTemplate(CommunicationType.WALLET_BALANCE_THRESHOLD + "|" + Channel.EMAIL, locale)
but this line return a freemarker.template.Template. I would like to have my original template back in order to avoid to make another query on db to get it.
Is that possible with Freemarker?
What you try to do here (if I understand it well) is making FreeMarker to cache application domain objects for you. This is not something the standard template loading/caching mechanism was designed for; the template library is a lower level layer that deals with its FreeMarker Template-s only. As you need multiple Template-s per domain object, you can't even bolt this together with Template.customAttributes and a custom TemplateConfigurerFactory (which can be used to attach arbitrary objects to Template-s based on the templateSource). So, I think what you should do is this:
Use any dedicated caching solution to cache your domain objects. The domain objects can store the Template objects directly (multiple of them in your case). Simply create those Template objects with the Template constructor when you create the domain object for a cache miss. Thus for those Template-s FreeMarker's TemplateLoader and cache isn't used. As far as they don't need to #include or #import each other (and thus FreeMarker need to be able to get them), that's fine.
For the sake of templates that the above templates need to #include or #import (i.e., common utility templates), set up a TemplateLoader and FreeMarker's cache will be used for them. As those are just templates, not some application domain objects, certainly you don't need to do anything tricky. (Furthermore such template are often stored in classpath resources or in a configuration directory, so perhaps you don't even need a DatabaseTemplateLoader.)
Related
I'm working on a project I didn't initially create, in which the data was stored in-memory. I'm curently moving this data into the database. I'm doing this using hibernate and tapestry JPA. At some point in the project Jackson Deserialization is used (actually in connection with a UI, but I doubt that's relevant), via the #JsonDeserialize annotation, with a deserializer class (let's call it DefinitionDeserializer). DefinitionDeserializer then creates an instance of a POJO representation (let's call it Definition) of a database table (D_DEFINITION). However, D_DEFINITION has a connection to another table (D_TYPE) (and hence another POJO (PeriodType)). To resolve this connection, I'm using a tapestry service (ConnectingService), which I usually inject via the #Inject annotation. However, I can't use this method of injection when the object (in which I'm trying to inject the service, i.e. DefinitionDeserializer) was created via the new keyword - which seems to be the case for the #JsonDeserialize annotation. I also can't use ConnectingService without injecting it via the #Inject keyword, because then I couldn't inject any other services in ConnectingService either, which I'm currently doing.
I'm hoping this description didn't confuse you too much, I can't share the actual code with you and I don't think a minimal example would be much better, as it's quite a complicated case and wouldn't be such a small piece of code. If you need one, however, I can try to provide one.
Basically what I need is a way to tell JsonDeserialize to take a tapestry service instead of creating an instance of DefinitionDeserializer itself.
Edit: The classes as examples:
public DefinitionDeserializer extends StdDeserializer<Definition> {
private static final long serialVersionUID = 1L;
//TODO: The injection doesn't work yet
#Inject
private ConnectingService connectingService;
public DefinitionDeserializer() {
this(null);
}
public DefinitionDeserializer(Class<?> vc) {
super(vc);
}
#Override
public Definition deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Definition pd = new Definition();
JsonNode node = p.getCodec().readTree(p);
if (node.has("type"))
pd.setType(periodTypeDao.findByValue("PeriodType." + node.get("type").asText()));
return pd;
}
}
#Entity
#Table(name = Definition.TABLE_NAME)
#Cacheable
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, region =
JpaEntityModelConstants.CACHE_REGION_ADMINISTRATION)
public class Definition {
public static final String TABLE_NAME = "D_DEFINITION";
private static final long serialVersionUID = 389511526676381027L;
#Id
#SequenceGenerator(name = JpaEntityModelConstants.SEQUENCE_NAME, sequenceName = JpaEntityModelConstants.SEQUENCE_NAME, initialValue = 1, allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = JpaEntityModelConstants.SEQUENCE_NAME)
#Column(name = "ID")
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "FK_TYPE", referencedColumnName = "ID")}
)
private PeriodType type;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public PeriodType getType() {
return type;
}
public void setType(PeriodType dpmType) {
this.type = dpmType;
}
//More columns
}
PeriodType looks pretty much the same as Definition.
//BaseService contains all the standard methods for tapestry JPA services
public interface ConnectingService extends BaseService<PeriodType> {
}
public class ConnectingServiceImpl extends BaseServiceImpl<PeriodType> implements ConnectingService {
public ConnectingServiceImpl() {
super (PeriodType.class);
}
}
Currently I'm using it like this (which doesn't work):
#JsonDeserialize(using = DefinitionDeserializer.class)
#JsonSerialize(using = DefinitionSerializer.class)
private Definition definition;
#JsonDeserialize doesn't create instances of deserialisers, it's just a hint for ObjectMapper to know which class to use when deserialising.
By default ObjectMapper uses Class.newInstance() for instantiating deserialisers, but you can specify custom HandlerInstantiator (ObjectMapper#setHandlerInstantiator()) in which you can use Tapestry's ObjectLocator to get instances of deserialisers, i.e. using ObjectLocator#autobuild(), or use ObjectLocator#getService() if your deserialisers are Tapestry services themselves.
Update:
public class MyHandlerInstantiator extends HandlerInstantiator
{
private final ObjectLocator objectLocator;
public MyHandlerInstantiator(ObjectLocator objectLocator)
{
this.objectLocator = objectLocator;
}
#Override
public JsonDeserializer<?> deserializerInstance(
DeserializationConfig config, Annotated annotated, Class<?> deserClass)
{
// If null returned here instance will be created via reflection
// you can always use objectLocator, or use it conditionally
// just for some classes
return objectLocator.autobuild(deserClass);
}
// Other method overrides can return null
}
then later when you're configuring ObjectMapper use #Injected instance of ObjectLocator to create an instance of custom HandlerInstantiator, i.e.:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setHandlerInstantiator(new MyHandlerInstantiator(objectLocator));
return objectMapper;
Reporting is a big part of our requirements and I have been tasked with creating a generic reporting framework that allows the User to specify which columns they would like data from (across numerous tables), which conditions to apply, and which output format they want the data in.
I will need to store this information into a 'Template' object so that I can generate the same Report over and over with consistent results. Once I finish I will give the Users the ability to specify a Reoccurence option to automatically invoke their 'Template' daily, weekly, monthly, or annually if they choose to enable it.
I want to avoid taking the SQL String as input to remove the risk of SQL Injection and I got something working, but it seems like there can be a much better way than the way I am doing it currently.
I created 4 types of Java classes to construct the Query.
Query: This is what the User will provide specifying their SQL in JSON.
Filter: This is used to specify a condition to be applied to the query.
Select: This is used to specify a column to be returned from the result.
Join: This is used to specify that a join should connect another table.
Note: I am validating all Table Names and Field Names against the Hibernate Table and Column annotations to ensure they are valid.
Some things that are missing are the ability to use aliases and NOT clauses, which I will want to add later.
I am using mySQL at the moment and my query doesn't need to be database agnostic. If I need to rewrite it if I move to another vendor than so be it.
--
// This is my RequestBody
public class Query {
private String from;
private Filter filter;
private List<Join> joins;
private List<Select> selections;
--
#ApiModel(value="filter", discriminator = "type", subTypes = {
JoinerFilter.class, MultiFilter.class, SimpleFilter.class
})
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = JoinerFilter.class, name = "joiner"),
#JsonSubTypes.Type(value = MultiFilter.class, name = "multi"),
#JsonSubTypes.Type(value = SimpleFilter.class, name = "simple")
})
public abstract class Filter {
public abstract void validate();
public abstract String toSQL();
}
--
// This Filter is used to concatenate 2 Filters
#ApiModel(value = "joiner", parent = Filter.class)
public class JoinerFilter extends Filter {
private enum JoinerCondition {
AND, OR
}
private JoinerCondition condition;
private Filter lhsFilter;
private Filter rhsFilter;
--
// This Filter is used to perform a simple evaluation
#ApiModel(value = "simple", parent = Filter.class)
public class SimpleFilter extends Filter {
private enum SimpleCondition {
EQUAL, GREATER_THAN, LESS_THAN, LIKE
}
private String table;
private String field;
private String lhsFunction;
private String rhsFunction;
private SimpleCondition condition;
private String value;
--
// This Filter is used to search multiple values at once
#ApiModel(value = "multi", parent = Filter.class)
public class MultiFilter extends Filter {
private enum MultiCondition {
BETWEEN, IN
}
private String table;
private String field;
private String lhsFunction;
private String rhsFunction;
private MultiCondition condition;
private List<String> values;
--
public class Select {
private String table;
private String field;
private String function;
--
public class Join {
private enum JoinType {
INNER_JOIN, LEFT_JOIN, CROSS_JOIN
}
private Filter on;
private String table;
private JoinType type;
I'm new to MongoDB and Reactor and I'm trying to retrieve a User with its Profiles associated
Here's the POJO :
public class User {
private #Id String id;
private String login;
private String hashPassword;
#Field("profiles") private List<String> profileObjectIds;
#Transient private List<Profile> profiles; }
public class Profile {
private #Id String id;
private #Indexed(unique = true) String name;
private List<String> roles; }
The problem is, how do I inject the profiles in the User POJO ?
I'm aware I can put a #DBRef and solve the problem but in it's documentation, MongoDB specify manual Ref should be preferred over DB ref.
I'm seeing two solutions :
Fill the pojo when I get it :
public Mono<User> getUser(String login) {
return userRepository.findByLogin(login)
.flatMap(user -> ??? );
}
I should do something with profileRepository.findAllById() but I don't know or to concatene both Publishers given that profiles result depends on user result.
Declare an AbstractMongoEventListener and override onAfterConvert method :
But here I am mistaken since the method end before the result is Published
public void onAfterConvert(AfterConvertEvent<User> event) {
final User source = event.getSource();
source.setProfiles(new ArrayList<>());
profileRepository.findAllById(source.getProfileObjectIds())
.doOnNext(e -> source.getProfiles().add(e))
subscribe();
}
TL;DR
There's no DBRef support in reactive Spring Data MongoDB and I'm not sure there will be.
Explanation
Spring Data projects are organized into Template API, Converter and Mapping Metadata components. The imperative (blocking) implementation of the Template API uses an imperative approach to fetch Documents and convert these into domain objects. MappingMongoConverter in particular handles all the conversion and DBRef resolution. This API works in a synchronous/imperative API and is used for both Template API implementations (imperative and the reactive one).
Reuse of MappingMongoConverter was the logical decision while adding reactive support as we don't have a need to duplicate code. The only limitation is DBRef resolution that does not fit the reactive execution model.
To support reactive DBRefs, the converter needs to be split up into several bits and the whole association handling requires an overhaul.
Reference : https://jira.spring.io/browse/DATAMONGO-2146
Recommendation
Keep references as keys/Id's in your domain model and look up these as needed. zipWith and flatMap are the appropriate operators, depending on what you want to archive (enhance model with references, lookup references only).
On a related note: Reactive Spring Data MongoDB comes partially with a reduced feature set. Contextual SpEL extension is a feature that is not supported as these components assume an imperative programming model and thus synchronous execution.
For the first point, I finally achieve doing what I wanted :
public Mono<User> getUser(String login) {
return userRepository.findByLogin(login)
.flatMap( user ->
Mono.just(user)
.zipWith(profileRepository.findAllById(user.getProfileObjectIds())
.collectionList(),
(u, p) -> {
u.setProfiles(p);
return u;
})
);
}
In my case, I have managed this problem using the follow approuch:
My Entity is:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Document(collection = "post")
public class Post implements Serializable {
private static final long serialVersionUID = -6281811500337260230L;
#EqualsAndHashCode.Include
#Id
private String id;
private Date date;
private String title;
private String body;
private AuthorDto author;
private Comment comment;
private List<Comment> listComments = new ArrayList<>();
private List<String> idComments = new ArrayList<>();
}
My controller is:
#GetMapping(FIND_POST_BY_ID_SHOW_COMMENTS)
#ResponseStatus(OK)
public Mono<Post> findPostByIdShowComments(#PathVariable String id) {
return postService.findPostByIdShowComments(id);
}
Last, but not, least, my Service (here is the solution):
public Mono<Post> findPostByIdShowComments(String id) {
return postRepo
.findById(id)
.switchIfEmpty(postNotFoundException())
.flatMap(postFound -> commentService
.findCommentsByPostId(postFound.getId())
.collectList()
.flatMap(comments -> {
postFound.setListComments(comments);
return Mono.just(postFound);
})
);
}
public Flux<Comment> findCommentsByPostId(String id) {
return postRepo
.findById(id)
.switchIfEmpty(postNotFoundException())
.thenMany(commentRepo.findAll())
.filter(comment1 -> comment1.getIdPost()
.equals(id));
}
Thanks, this helped a lot.
Here is my solution:
public MappingMongoConverter mappingMongoConverter(MongoMappingContext mongoMappingContext) {
MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mongoMappingContext);
converter.setTypeMapper(new DefaultMongoTypeMapper(null));
converter.setCustomConversions(mongoCustomConversions());
return converter;
}
The trick was to use the NoOpDbRefResolver.INSTANCE
I am using Hibernate and currently using the setter to set the relation to parent in children at creation time (to avoid doing this manually for both sides). How I can avoid use of setter or avoid expose it to the rest of classes and get the same behaviour. Is it ok to use reflection? This is the code:
#Entity
#Table(name = "TEST_GROUP")
#Getter
public class TestGroupEntity extends AuditedEntity{
#ManyToOne
#JoinColumn(name = "owner", nullable = false)
protected UserEntity owner;
#Column(name = "description")
#Setter
protected String description;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
protected Set<TestEntity> tests = Sets.newHashSet();
public boolean addTest(TestEntity testEntity) {
return tests.add(testEntity);
}
public boolean removeTest(TestEntity testEntity) {
return tests.remove(testEntity);
}
public TestGroupEntity(UserEntity owner, Set<TestEntity> tests) {
this.owner = owner;
owner.setTestGroupEntity(this); ! how to avoid creation of setter
this.tests = tests;
tests.stream().forEach(t -> t.setTestGroupEntity(this)); ! how to avoid creation of setter
}
}
This is the children class ( I would like to keep immutability on api level):
#MappedSuperclass
#AllArgsConstructor
public class TestEntity extends AuditedEntity {
#Column(name = "name", nullable = false)
protected String name;
#Column(name = "description")
protected String description;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "test_group", nullable = false)
protected TestGroupEntity testGroupEntity;
public void setTestGroupEntity(TestGroupEntity testGroupEntity) {
this.testGroupEntity = testGroupEntity;
}
}
Edit: I think commented sections of code was not visible. Sorry.
How I can avoid use of setter or avoid expose it to the rest of
classes and get the same behaviour. Is it ok to use reflection?
Of course you can for example reduce visibility of public setters to a visibility less wide than public in order that client classes of your entities cannot use them.
Which is in your case the real problem since accessing any data from inside the object is possible in anyway
From hibernate doc :
Attributes (whether fields or getters/setters) need not be declared
public. Hibernate can deal with attributes declared with public,
protected, package or private visibility. Again, if wanting to use
runtime proxy generation for lazy loading the visibility for the
getter/setter should be at least package visibility.
So, try to use private setter for desired field. It should address your problem.
Update After comment
You have several workarounds to address your problem :
using reflection (your basic idea).
Drawback : it brings a little complexity, not a full check at compile-time and at last, someone who sees your code could wonder why you used that...
It is the same thing for any concepts which relies on reflection such as AOP.
declaring these setters with package-private level and put the 3 classes in the same package. Drawback : the used package.
creating public init methods which raises an exception if it used more than once for a same object. In this way, you guarantee the coherence of the object if bad used. Drawback : method which should not be used by clients is still provided to clients.
Unfortunately, you have not a perfect solution since Java visibility mechanisms cannot provide a ready-to-use solution for what you are looking for.
Personally, I prefer reflection or init method solutions.
Personally, I noticed that in based-class languages as Java, a good developer has often the reflex to over- protect accessibility of objects/methods. In fact, in many cases, it is not needed because it will not break the application or data integrity.
Here an example with init method :
public TestGroupEntity(UserEntity owner, Set<TestEntity> tests) {
this.owner = owner;
owner.constructInit(this);
this.tests = tests;
tests.stream().forEach(t -> t.constructInit(this));
}
public class UserEntity {
private TestGroupEntity testGroupEntity;
public void constructInit(TestGroupEntity testGroupEntity) {
if (this.testGroupEntity != null) {
throw new IllegalArgumentException("forbidden");
}
this.testGroupEntity=testGroupEntity;
}
}
Make a constructor in your parent class and call it from child.
Here is the parent constructor looks like
public AuditedEntity(UserEntity owner, Set<TestEntity> tests){
this.owner = owner;
this.tests = tests;
}
And change your child constructor like
public TestGroupEntity(UserEntity owner, Set<TestEntity> tests) {
super(owner,tests);
}
I am not sure what the best practice is for dealing with collection/lookup tables/in RequestFactory.
For example if I have following two Domain objects:
#Entity
public class Experiment {
private Long id;
private String name;
#ManyToOne(cascade={CascadeType.PERSIST,CascadeType.MERGE})
private UnitOfMeasure unitOfMeasure;
public Experiment() { }
public String getName() {
return name;
}
public Long getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public UnitOfMeasure getUnitOfMeasure() {
return unitOfMeasure;
}
public void setUnitOfMeasure(UnitOfMeasure unitOfMeasure) {
this.unitOfMeasure = unitOfMeasure;
}
}
#Entity
public class UnitOfMeasure {
private Long id;
private String unit_type;
public UnitOfMeasure() { }
public String getUnitType() {
return unit_type;
}
public Long getId() {
return id;
}
public void setUnitType(String unitType) {
this.unit_type = unitType;
}
}
This is a normal unidirectional 1:n realtionship between Experiment and UnitOfMeasure using a ForeignKey in the Experiment table.
I have a limited amount of different UnitOfMeasure instances which usually don't change.
The web-app provides a view where the user can change some properties of the Experiment instance. The view uses the Editor framework. For changing the UnitOfMeasure of a specific Experiment I use a ValueListBox and render the unit_type property.
Because the list of available UnitOfMeasure instances is static I use AutoBeanFactory to create a json string which I put into the HTML host page and during application start I parse it (same thing for all other collection like table values) and store them in a Singleton class instance (AppData) which I pass to `setAcceptableValues``.
Currently I derive UnitOfMeasureProxy from EntityProxy but in order to decode/encode it with AutoBeanFactory I have to annotate the Factory with EntityProxyCategory. I somehow suspect that a ValueProxy would be a better fit.
However with a ValueProxy when I change the UnitOfMeasure of a specific Experiment the entire ValueProxy instance is transmitted over the wire.
From a database point of view however only changing the value for the foreignkey in the Experiment table is required.
So what is the best practice (ValueProxy vs EntityProxy) for collection like tables and child values respectively?
In many cases, references to other entities are best modelled using their IDs rather than the EntityProxys themselves (it's debatable, but I think it's also true for server-side code, or actually any code that crosses unit-of-work boundaries –JPA EntityManager lifetime, Hibernate session, etc.–)
BTW, the proper way to serialize RequestFactory proxies is to use a ProxySerializer.
Make sure you use GWT 2.5.0-rc1 though if you have lists of ValueProxys (see issue 6961)