Is it possible to get previous entry in #HandleAfterSave event with Spring Data REST? - spring-data-rest

I have next event handler:
#Component
#RepositoryEventHandler
public class EntityEventHandler {
private final EntityService entityService;
#HandleAfterSave
public void handleAfterSave(final Entity entity) {
// need old entity here to validate if specific field has changed
}
}
Is there any way to get old entity in handleAfterSave ?

Related

.NET 5 Web API: Storing data per request

When getting a request in any action of any controller, I look at the jwt know which user is requesting and lookup the user in the database to get some user-data that I want to use throughout the application. E.g. which departments the user belongs to or the users preferred language.
Now I could create a object which wraps these information and send it down the layers and pass it to every method that likes to use some of this data. But I like the data to be available to every method throughout the application without passing it in every method. Like e.g. dependency injection (Seems to late at that point) or something else I can get access to that data quickly.
Any advice of how to handle it?
Try it with the Items property on the HttpContext. By using it you can store data during a single request. The only downside with this approach is that every service needs to have access to the HttpContext to read the values. Values can be added to the Items Dictionary as shown below
public class IndexModel : PageModel
{
//
public void OnGet()
{
HttpContext.Items.Add("Key", new RequestInfo { Key = "RequestKey" });
}
}
class RequestInfo
{
public string Key { get; set; }
}
You can then access the value by registering the IHttpContextAccessor to the IServiceCollection and then using Constructor injection to use the HttpContext in your service so that you can work with the Items Dictionary.
public class Service
{
private IHttpContextAccessor _htp;
public Service(IHttpContextAccessor http)
{
_htp = http;
}
public void Log()
{
Console.WriteLine(((RequestInfo)_htp.HttpContext.Items["Key"]).Key);
}
}

Invoking custom Jackson deserializers inside of other deserializers

I am writing a Spring Boot app (RESTful webservice) that uses Jackson for serialization. I have the following data models that will be sent back and forth between the service and its HTTP clients (hence these will be serialized/deserialized to/from JSON):
public abstract class BaseEntity {
#JsonIgnore
private Long id;
private UUID refId;
// Getters, setters, ctors, etc.
}
public abstract class BaseLookup extends BaseEntity {
private String name;
private String label;
private String description;
// Getters, setters, ctors, etc.
}
public class State extends BaseLookup {
private String abbrev; // "VT", "FL", etc.
// Getters, setters, ctors, etc.
}
public class Contact extends BaseEntity {
private String givenName;
private String surname;
private State state;
// Getters, setters, ctors, etc.
}
public class Account extends BaseEntity {
private Contact contact;
private String code;
// lots of other fields that will be generated server-side
// Getters, setters, ctors, etc.
}
Thus there will be some endpoints for CRUDding Accounts, others for CRUDding Contacts, etc. For instance, the AccountController will expose endpoints for CRUDding Account instances:
#RestController
#RequestMapping(value = "/accounts")
public class AccountController {
#RequestMapping(method = RequestMethod.POST)
public void createAccount(#RequestBody Account account) {
// Do stuff and persist the account to the DB
}
}
I want to simplify the JSON that HTTP clients must craft in order to create new Account, Contact, etc. instances. At the same time there are fields on those data models that I do not want exposed to the client-side. Things like the BaseEntity#id (which is the PK of the entity in the DB). Or for instance, in the case of State, I just want the client-side to know about (and use) the abbrev field, etc. I don't want them to ever see the other BaseLookup fields or even know about them.
Hence, my end goal is to allow the client to POST the following JSON, and have a custom Jackson deserializer convert that JSON into an Account instance:
{
"contact" : {
"givenName" : "Him",
"surname" : "Himself",
"state" : "NY"
},
"code" : "12345"
}
So you see, like I stated above, this JSON accomplishes several things:
The client-side doesn't provide a BaseEntity#id or BaseEntity#refId when POSTing to create a new instance
For the contact.state field, which is a BaseLookup with several fields (id, refId, name, label, description, abbrev), the user only has to provide the abbrev field, and the deserializer is expected to figure out which State the client is referring to
The Account class actually has many other fields that are inferred/generated server-side; the client doesn't need to know about them in order to create an Account instance
The JSON above is a simplified form of what we would get if we serialized an Account with Jackson's default behavior; this is to make things easier on the client-side and even more secure on the server-side (not exposing PKs, etc.)
The important thing to note here is that the JSON sent to this controller for the contact field is identical to the JSON that will be POSTed to a ContactController for creating new Contact instances.
Here's the problem:
public class AccountDeserializer extends StdDeserializer<Account> {
public AccountDeserializer() {
this(null);
}
public AccountDeserializer(Class<Account> accClazz) {
super(accClazz);
}
#Override
public Account deserialize(JsonParser jsonParser, DeserializationContext dCtx)
throws IOException, JsonProcessingException {
JsonNode jsonNode = jsonParser.codec.readTree(jsonParser)
Contact contact = ??? // TODO: How to invoke ContactDeserializer here?
String accountCode = node.get("code").asText();
// Generate lots of other Account field values here...
Account account = new Account(contact, accountCode, /* other fields here */);
return account;
}
}
Since I will also have a ContactController (for CRUDding Contact instances irrespective of an associated Account), and because I have similar desires to hide Contact fields from the client-side as well as to simplify the JSON coming into this ContactController#createContact endpoint, I will also need a ContactDeserializer in addition to this AccountDeserializer...
public class ContactDeserializer extends StdDeserializer<Contact> {
// ...etc.
}
This ContactDeserializer will be responsible for converting JSON into Contact instances. But since Account instances also contain Contact instances, and because the "contact JSON" inside the outer "account JSON" will be the same as any JSON that the client sends to any of the "contact endpoints", I'd like to invoke the ContactDeserializer from inside the AccountDeserializer somehow.
That way, when the ContactController receives "contact JSON" to create a new Contact instance, the ContactDeserializer is engaged to get the job done. And, if the AccountController receives "account JSON" to create a new Account instance, then the AccountDeserializer is engaged to get that job done...and it uses the ContactDeserialzer to handle the deserialization of the account JSON's internal contact field as well.
Can this be done?! Can one Jackson deserializer reuse other deserializers inside of it? If so, how? If not, then what's the solution here?!
You can invoke ContactDeserializer by calling the treeToValue method of ObjectCodec. Jackson will automatically pick up the ContactDeserializer for you if you've registered it on your ObjectMapper.
public class AccountDeserializer extends JsonDeserializer<Account> {
#Override
public Account deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
JsonNode node = p.readValueAsTree();
JsonNode contactNode = node.get("contact");
Contact contact = null;
if (contactNode != null) {
contact = p.getCodec().treeToValue(contactNode, Contact.class);
}
return new Account(contact, /* account properties */);
}
}
Edit
If you want to add your deserializers to existing mapper which is created by Spring Boot, you can autowire it in one of your configuration classes and customize as you like.
#Configuration
public class ObjectMapperConfiguration {
#Autowired
public void configureObjectMapper(ObjectMapper mapper) {
SimpleModule module = new SimpleModule()
.addDeserializer(Account.class, new AccountDeserializer())
.addDeserializer(Contact.class, new ContactDeserializer());
mapper.registerModule(module);
}
}

Circular dependency when mapping parent and child entities to DTOs

I just implemented explicitly loading child entities along with their parents using in a generic repository using jevelez's method shown in this question.
However now i'm trying to map the child entities along with their parent entities to DTO's so i can send them to my UI layer.
Here's how my mappers currently look like:
The mapping interface:
public interface IMappingService<TEntity, TDto>
where TEntity : class
where TDto : class
{
TDto EntityToDto(TEntity entity);
TEntity DtoToEntity(TDto dto);
IEnumerable<TDto> EntitiesToDtos(IList<TEntity> entities);
IEnumerable<TEntity> DtosToEntities(IList<TDto> dtos);
}
The abstract base method that implements the interface:
public abstract class MapperBase<TEntity, TDto> : IMappingService<TEntity, TDto>
where TEntity : class
where TDto : class
{
public abstract TDto EntityToDto(TEntity entity);
public abstract TEntity DtoToEntity(TDto dto);
public virtual IEnumerable<TDto> EntitiesToDtos(IList<TEntity> entities)
{
return entities.Select(EntityToDto);
}
public virtual IEnumerable<TEntity> DtosToEntities(IList<TDto> dtos)
{
return dtos.Select(DtoToEntity);
}
}
Here's are two mappers that implement these for two entities as an example:
public class ParentEntityMapper : MapperBase<ParentEntity, ParentEntityDto> , IParentEntityMapper
{
private readonly IChildEntityMapper _childEntityMapper;
public ParentEntityMapper(IChildEntityMapper childEntityMapper)
{
_childEntityMapper = childEntityMapper;
}
public override ParentEntityDto EntityToDto(ParentEntity entity)
{
var dto = new ParentEntityDto();
dto.Id = entity.Id;
dto.Title = entity.Title;
dto.Description = entity.Description;
if (entity.ChildEntities != null)
{
dto.ChildEntities= _childEntityMapper.EntitiesToDtos(entity.ChildEntities .ToList()).ToList();
}
return dto;
}
public override ParentEntity DtoToEntity(ParentEntityDto dto)
{
// This is just a reverse of the above function
}
}
And the child entity mapper:
public class ChildEntityMapper : MapperBase<ChildEntity, ChildEntityDto>, IChildEntityMapper
{
private readonly ParentEntityMapper _parentEntityMapper;
public ChildEntityMapper(IParentEntityMapper parentEntityMapper)
{
_parentEntityMapper = parentEntityMapper;
}
public override ChildEntityDto EntityToDto(ChildEntity entity)
{
var dto = new ChildEntityDto();
dto.Id = entity.Id;
dto.Description = entity.Description;
if (entity.ParentEntity != null)
{
dto.ParentEntity = _parentEntityMapper.EntityToDto(entity.Image);
}
return dto;
}
public override Anchor DtoToEntity(AnchorDto dto)
{
// Just a reverse of the above function
}
}
So i sure you all can see where the circular dependency occurs. I've been agonizing over this for the past 2 days but I'm still pretty amateur at this and haven't been able to figure this out using Google thus far.
I prefer not to use tools like AutoMapper and ValueInjector for this part since i'm mapping between the database and DTO's and I'm already using it to map between the DTO's and the ViewModels in the UI layer.
If possible I'd like to be able to keep the mapping between the two this way since sometimes I'll be requesting just the child entities and in those cases I might want to get the parent entity along with it.
Does anyone know how this can be done properly?
I think I see where your "circular dependency" is coming in to play here, but just to be explicit, here's what I think would happen. You call EntityToDto on the ParentEntity, which in turn calls EntityToDto on all of the ChildEntity elements. Each of these ChildEntity elements in turn call EntityToDto on the ParentEntity and we are back to where we started from.
If that's the case my recommendation would be simple: don't have the child entity care EntityToDto on the parent. I would resolve at the root of the tree and then let everything below continue to resolve downward until the tree is complete.
If you did it this way, it would prevent the child entity from assigning the parent to itself. If you need to be able to traverse from the child to the parent, I'd have the parent assign itself to the child as the parent after the EntityToDto call was complete.
After thinking about it further i decided that instead of over complicating it that i would'nt allow getting a child entity with it's parent through the mapper but instead handle it for those few but specific scenarios in my service layer.
Anyone having the same problem can also check this thread i created on reddit for more information.

Wicket: how to get rid of WicketNotSerializableException?

Ok, so here is the code for the page
public class ViewDocument extends BasePage{
private List<WeinSyncFileContent> transactions;
....
public ViewDocument(){
transactions = ....;
....
listContainer.add(listView = new ListView<WeinSyncFileContent>("transactions", transactions){
#Override
public void populateItem(final ListItem<WeinSyncFileContent> item)
{
....
}
});
}
}
The page does get displayed but I get errors:
Error serializing object class kz.wein.wicket.pages.documents.ViewDocument
and it's complaining about the transactions field:
transactions [class=java.util.ArrayList$SubList] <----- field that is not serializable
Also I want to note that objects I am displaying in the list are initially taken from library and are not serializable. Wicket wants serializable objects inside lists so to deal with it I take each object and make it serializable with class like this
public class WeinSyncFileContent extends SyncFileContent implements Serializable{
public WeinSyncFileContent(SyncFileContent obj){
... setting fields ...
}
}
so initially I get SyncFileContent objects (that are not serializable)
What can I do about the errors?
You are getting this error because any field level variables in your Wicket pages will be serialized. So its probably a good idea to not have any non-serializable objects as field level variables. There must be an object in your WeinSyncFileContent that is not serializable which is why you are getting this error.
You may want to instead use models to load your list so something like:
public ViewDocument(){
...
listContainer.add(new ListView<WeinSyncFileContent>(
"transactions",
new LoadableDetachableModel<List<WeinSyncFileContent>>() {
protected List<WeinSyncFileContent> load() {
return ...;
}
})
{
#Override
public void populateItem(final ListItem<WeinSyncFileContent> item)
{
....
}
});
}

Does StructureMap have scoping corresponding to NInject's DefinesNamedScope/InNamedScope?

The problem I'd like to solve is sharing an ISessionProvider between IXyzRepositories (where ISessionProvider holds the current NHibernate ISession).
I'm tweaking the "Setting up session per presenter" recipe from NHibernate 3 Cookbook, and would like to keep StructureMap (brownfield project).
I think you would have to create a custom Lifecyle to do that, although I am not sure what exactly you are trying to accomplish...
To create a custom Lifecycle, you just have to implement the ILifecycle interface and the use it in your registration. Here is an example you can look at: http://blog.mikeobrien.net/2010/01/creating-structuremap-lifecycle-for-wcf.html.
In a web application I use Singleton for the sessionFactory and HybridHttpOrThreadLocalScoped for the session:
This is my structuremap registry:
public class NhibernateRegistry: Registry
{
public NhibernateRegistry()
{
For<ISessionFactory>()
.Singleton()
.Use(new NHibernateSessionFactory(connectionString).SessionFactory);
For<ISession>()
.HybridHttpOrThreadLocalScoped()
.Use(o => o.GetInstance<ISessionFactory>().CurrentSession);
}
}
My NHibernateSessionFactory is similar to SessionProvider class used in the book.
Everything is disposed at the end of the request (web app):
protected void Application_EndRequest(object sender, EventArgs e)
{
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}
I use a generic repository:
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
private readonly ISession _session;
public GenericRepository(ISession session)
{
_session = session;
}
public T Load(Guid Code)
{
return (_session.Load<T>(Code));
}
}
but you can easily change it with your own implementation.
I register the repository here:
public class RepositoriesRegistry : Registry
{
public RepositoriesRegistry()
{
For <Data.IRepository<Domain.Reminder, Guid>>()
.HybridHttpOrThreadLocalScoped()
.Use<Data.NH.Repository<Domain.Reminder, Guid>>();
}
}