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;
Related
I have a POJO used by an existing endpoint, this endpoint responds with all fields present in the POJO.
But I'm creating a new enpoint which should respond only some of the fields of the same POJO.
I want to avoid copying the same POJO file and deleting the parameters I don't need, is there a way to do this?
This is the POJO:
public class AgentChatStatus {
private UUID activeChat;
private List<AgentChat> chatRequests; //Object with less params on new endpoint
private List<AgentChat> chatsOnHold; //Object with less params on new endpoint
private Collection<Agent> agents;
private int totalChatRequests;
private int totalChatsOnHold;
private Preferences.AgentConsoleConfig config;
// ...
}
public class AgentChat implements Payload {
private UUID id;
private String queueId;
Lets say I only need to show "Id" in endpoint 2 but id and queueId in endpoint1.
I work with spring btw.
Thanks!
With Jackson you can take advantage of JSON Views. The first thing is to create the Views as follows:
public class Views {
public static class ViewEndpoint2 { // The name can be whatever you want
}
public static class ViewEndpoint1 extends ViewEndpoint2 {
}
}
Then you need to annotate the properties in your POJO with #JsonView so that you tell Jackson in which views such properties should be visible:
public class AgentChatStatus {
#JsonView(Views.ViewEndpoint2.class)
private UUID activeChat;
#JsonView(Views.ViewEndpoint2.class)
private List<AgentChat> chatRequests;
#JsonView(Views.ViewEndpoint2.class)
private List<AgentChat> chatsOnHold;
#JsonView(Views.ViewEndpoint2.class)
private Collection<Agent> agents;
#JsonView(Views.ViewEndpoint2.class)
private int totalChatRequests;
#JsonView(Views.ViewEndpoint2.class)
private int totalChatsOnHold;
#JsonView(Views.ViewEndpoint2.class)
private Preferences.AgentConsoleConfig config;
}
public class AgentChat implements Payload {
#JsonView(Views.ViewEndpoint2.class)
private UUID id;
#JsonView(Views.ViewEndpoint1.class)
private String queueId;
}
Finally, you need to annotate the endpoints with the corresponding view:
#JsonView(Views.ViewEndpoint1.class)
#RequestMapping("your-old-enpoint")
public void yourOldEndpoint() {
(...)
}
#JsonView(Views.ViewEndpoint2.class)
#RequestMapping("your-new-enpoint")
public void yourNewEndpoint() {
(...)
}
#JsonView(Views.ViewEndpoint1.class) basically means all properties in AgentChatStatus and AgentChat and #JsonView(Views.ViewEndpoint2.class) means only some of them (the ones annotated with #JsonView(Views.ViewEndpoint2.class)).
You can read more about this at https://www.baeldung.com/jackson-json-view-annotation.
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.)
I have #Entity classes in an external package that also have static metamodels. In my application's service class, I am using those metamodels and the EntityManager/CriteriaBuilder/CriteriaQuery to retrieve my data. This works fine when running the application. However, when running unit tests, my metamodels and their attributes are always null.
Code...
package com.example.core.entities;
#Entity
#Table(schema = "lookup", name="BookingSystem")
public class BookingSystem implements ILookupEntity, IAuditEntity, Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id")
public Integer id;
#Column(name = "name")
public String name;
#Column(name = "code")
public Integer code;
}
package com.example.core.entities;
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(BookingSystem.class)
public abstract class BookingSystem_ {
public static volatile SingularAttribute<BookingSystem, Integer> id;
public static volatile SingularAttribute<BookingSystem, Integer> code;
public static volatile SingularAttribute<BookingSystem, String> name;
}
Usage in my app's service class...
package com.example.bookingsystem;
#Service
public class BookingService {
#PersistenceContext
private EntityManager entityManager;
public void saveBooking(Booking booking) {
//...
RepositoryQueryBuilder<BookingSystem> bookingSystemSelector = new RepositoryQueryBuilder<>(entityManager, BookingSystem.class);
List<BookingSystem> bookingSystems = bookingSystemSelector
.and(BookingSystem_.code, booking.bookingSystem.code) //<-- Here "BookingSystem_.code" is null.
.getResultList();
//...
}
}
The "RepositoryQueryBuilder" class is just a utility builder class that wraps an EntityManager, CriteriaBuilder, etc. Basically modeled after this example...
JPA Criteria Predicate Conditions
Unit test code...
package com.example.bookingsystem;
public abstract class BaseTestSetup {
#InjectMocks
protected BookingService bookingService;
protected EntityManager entityManager = PowerMockito.mock(EntityManager.class);
protected CriteriaBuilder criteriaBuilder = PowerMockito.mock(CriteriaBuilder.class);
protected CriteriaQuery<BookingSystem> criteriaQuery = PowerMockito.mock(CriteriaQuery.class);
protected Root<BookingSystem> root = PowerMockito.mock(Root.class);
protected void arrange() {
when(entityManager.getCriteriaBuilder()).thenReturn(criteriaBuilder);
when(criteriaBuilder.createQuery(BookingSystem.class)).thenReturn(criteriaQuery);
when(criteriaQuery.from(Matchers.<Class<BookingSystem>>any())).thenReturn(root);
when(criteriaQuery.from(Matchers.<EntityType<BookingSystem>>any())).thenReturn(root);
}
}
#RunWith(PowerMockRunner.class)
public class BookingServiceTest extends BaseTestSetup {
#BeforeClass
#Override
public void arrange() {
super.arrange();
//...
}
#Test
public void doIt() {
Booking booking = new Booking();
booking.id = 12345;
booking.bookingSystem = new BookingSystem();
booking.bookingSystem.id = 1;
booking.bookingSystem.code = 106000;
bookingService.saveBooking(booking);
}
}
I've looked at this JPA/Hibernate Static Metamodel Attributes not Populated -- NullPointerException, but the solution seems to be "make sure that the entity and its metamodel are in the same package", but as you can see, both are already in my "com.example.core.entities" package.
I'm using all bean and annotation driven configruation in my code (no persistence or context xml files). As far as testing goes, I'm using TestNG and PowerMock from within IntelliJ.
It just seems as if the metamodels aren't being picked up during unit tests. Any ideas.
The static metamodel classes are populated when hibernate is loaded. So, either you configure hibernate context in your test or you populate the attributes manually before the method execution. In you code, you could do:
#Test
public void doIt() {
BookingSystem_.code = new SingularAttributeMock<BookingSystem, Integer>();
bookingService.saveBooking(booking);
}
}
The class SingularAttributeMock can be created custom-made in order to use it in your tests. You can also use any other implementation of the SingularAttribute class.
public class SingularAttributeMock<X, Y> implements SingularAttribute<X, Y> {
//Overriding methods of SingularAttribute...
}
Instead of creating own class, I suggest making Mockito to do the job for you.
#Mock // declare a mock along the others you might have
private SingularAttribute<BookingSystem, Integer> code;
#Before
public void setUp() throws Exception {
// fill metamodel with it
BookingSystem_.code = code;
}
Worth to mention however that bringing metamodels to service layer is not very good practice, you would rather push them down to DAO methods.
There is no need to manual initialization.
You should observe following rules :
Metamodel classes should declared as abstract class.
Metamodel classes should be in the same package as the entity classes they
describe;
They should have the same name as the entity classes they
describe, followed by an underscore (e.g. Product is the entity,
Product_ is the metamodel class);
If an entity inherits from another
entity or from a mapped superclass, its metamodel class should
inherit from the metamodel class that describes its immediate
superclass (e.g. if SpecialProduct extends Product, which extends
PersistentObject, then SpecialProduct_ should extend Product_ which
should extend PersistentObject_).
In my case, mock the metamodel doesn't worked, so I just get it from entityManager.
private EntityManager entityManager;
#Before
public void init() {
// Assume that entityManager was correctly initialized.
this.configMetaModel();
}
private void configMetaModel() {
Metamodel metamodel = this.entityManager.getMetamodel();
BiFunction<EntityType<MY_ENTITY>, String, Attribute>
bfAttr = (entity, field) -> entity.getAttributes()
.stream()
.filter(a -> a.getName().equals(field))
.findAny()
.get();
Function<Attribute, SingularAttribute<MY_ENTITY, String>> fToStr = attribute ->
(SingularAttribute<MY_ENTITY, String>) attribute;
Function<Attribute, SingularAttribute<MY_ENTITY, LocalDate>> fToDate = attribute ->
(SingularAttribute<MY_ENTITY, LocalDate>) attribute;
EntityType<MY_ENTITY> entity = metamodel.entity(MY_ENTITY.class);
MY_ENTITY_.id = fToStr.apply(bfAttr.apply(entity, "id"));
MY_ENTITY_.name = fToStr.apply(bfAttr.apply(entity, "name"));
MY_ENTITY_.someDate = fToDate.apply(bfAttr.apply(entity, "someDate"));
}
"MY_ENTITY" replace my entity
"MY_ENTITY_" replace my entity metamodel
Once I did this, I could run all my unit test perfectly.
I am working on a Spring-MVC application in which depending upon the mode set by the user, I have to return a List of either Object1 or Object2. Ideally, I can create two controller methods and send the List appropriately, but I would like to know is there any way, I can send any type of List in that Controller method.
Controller method :
#PreAuthorize("hasRole('ROLE_USER')")
#RequestMapping(value = "/findnotebydays/{days}/{canvasid}/{mode}")
public #ResponseBody List<Inotes> findNotesByDays(#PathVariable("days")int days, #PathVariable("canvasid")int canvasid,
#PathVariable("mode")boolean mode ){
if(!mode){
return this.groupNotesService.findGroupNotesByDays(days,canvasid);
} else {
return this.notesService.findNotesByDays(days,canvasid);
}
}
Basically, if mode is false, I want to return List<GroupNotes> and if mode is true, I would like to return List<Notes>. My naive approach that I thought I can just say it is an Object and return, but doesn't seem to work. Kindly let me know what I can do. Thanks a lot. :-)
Update
GroupNotes model class :
#Entity
#Table(name="groupnotes")
public class GroupNotes implements Inotes{
#Id
#Column(name="mnoteid")
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "mnote_gen")
#SequenceGenerator(name = "mnote_gen",sequenceName = "mnote_seq")
#org.hibernate.annotations.Index(name = "mnoticesidindex")
private int mnoticesid;
#Column(name = "mnotetext")
private String mnotetext;
//Other variables, getters, setters ignored
}
Notes model class :
#Entity
#Table(name="note")
public class Notes implements Inotes{
#Id
#Column(name="noteid")
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "note_gen")
#SequenceGenerator(name = "note_gen",sequenceName = "note_seq")
#org.hibernate.annotations.Index(name = "noteidindex")
private int noticesid;
#Column(name = "notetext")
private String notetext;
//Other variables, getters, setters ignored
}
Interface Inotes :
package com.journaldev.spring.model;
public interface Inotes {
}
Following the clean coder style, if you have to pass a boolean to a method, that method actually does two different things, and you want a method to do only one thing.
I would just make two controller actions, it is much clearer to the user and to the maintainer of the code later on.
You should probably use a common interface shared by both classes.
Declaring an interface like, for instance:
public interface INotes{
}
public class GroupNotes implements INotes{...}
public class Notes implements INotes{...}
Your code would become:
#PreAuthorize("hasRole('ROLE_USER')")
#RequestMapping(value = "/findnotebydays/{days}/{canvasid}/{mode}")
public #ResponseBody List<INotes> findNotesByDays(#PathVariable("days")int days, #PathVariable("canvasid")int canvasid,
#PathVariable("mode")boolean mode ){
if(!mode){
return this.groupNotesService.findGroupNotesByDays(days,canvasid);
} else {
return this.notesService.findNotesByDays(days,canvasid);
}
}
Another simpler solution would be just returning List (or List<?> for compliance) but this approach is more appropriated.
Considering Notes and GroupNotes are both implementing the Note interface (for example), you could always return List<Note>.
I want to store a property into the database as a Long, but use the object with helper methods in the code.
However the object type is a custom type I have that has an internal value (a long) that I want to store to the database.
public final class MyBean extends Number implements Serializable, Comparable<MyBean>
{
private long myValue;
public MyBean(Long value) { this.myValue = value; }
// Other helper methods and overrides
public MyBean valueOf(Long data)
{
return new MyBean(data);
}
#Override
public String toString()
{
return String.valueOf(myValue);
}
}
This is how I am using it:
#Entity
#Table(name = "mybeans")
public class MyBean implements Serializable
{
private static final long serialVersionUID = 1L;
MyBean myBean;
#Id
#Column(name = "mybean", nullable = false)
public MyBean getMyBean() { return myBean; }
public void setMyBean(MyBean value) { this.myBean = value; }
}
Deserializing this object calls toString and works fine (jax-rs/jersey). But when I try to pull it out of the database using my EJB, the error I get is:
The object [1,427,148,028,955], of class [class java.lang.Long], could
not be converted to [class com.MyBean]
Saving it produced the error
Can't infer the SQL type to use for an instance of com.MyBean. Use
setObject() with an explicit Types value to specify the type to use.
Which makes sense.
But what methods can I add in to male the EJB get the long as the value and use the long to set up a new object?
ANSWER:
Making the class #Embeddable and adding the following attributes worked.
#Embedded
#AttributeOverrides({
#AttributeOverride(name="value", column=#Column(name="mybean"))
})
(I didn't add EmbeddedId because I added a serial primary key id and just made this a column)
The one caveat is that it won't work with dynamic weaving. I had to add
<property name="eclipselink.weaving" value="static"/>
to my persistence.xml
You can try making MyBean an Embeddable to use that as an EmbeddedId, something like this:
#Embeddable
public final class MyBean extends Number implements Serializable, Comparable<MyBean> {
private Long myValue;
public MyBean(Long myValue) {
this.myValue = myValue;
}
// Other helper methods and overrides
public MyBean valueOf(Long data) {
return new MyBean(data);
}
#Override
public String toString() {
return String.valueOf(myValue);
}
}
In your entity, MyBean will be an EmbeddedId and will look like this:
#Entity
#Table(name = "mybeans")
public class MyEntity implements Serializable {
private static final long serialVersionUID = 1L;
private MyBean myBean;
#EmbeddedId
#AttributeOverride(name="myValue", #Column(name="mybean_id"))
public MyBean getMyBean() {
return myBean;
}
public void setMyBean(MyBean myBean) {
this.myBean = myBean;
}
}
Adjust MyBean as you need, such as making Transient some attributes.