Why is my projection interface not picked up by Spring Data REST? - spring-data

I am trying to use up projections with Spring Data REST (version 2.3.0.RELEASE). I read the reference documentation, and gathered that these are the parts I need:
A JPA Entity
#Entity
public class Project implements Serializable {
#Basic(optional = false)
#Column(name = "PROJECT_NAME")
private String projectName;
// ... lots and lots of other stuff
}
A repository that works with that entity
#Repository
public interface ProjectRepository extends JpaRepository<Project, Long> { }
And a projection to retrieve just the name for that entity
#Projection(name="names", types={Project.class})
public interface ProjectProjectionNamesOnly {
String getProjectName();
}
I would like to be able to optionally retrieve just a list of names of projects, and projections seemed perfectly suited to this. So with this setup, I hit my endpoint at http://localhost:9000/projects/1?projection=names. I get back ALL of the attributes and collections links, but I expected to get back just the name and self link.
I also viewed the sample project on projections, but the example is for excerpts, which seems different from projections as it is a different section of the reference. I tried it and it didn't work anyway though.
So the question is this: How do you use spring data rest projections to retrieve just a single attribute of an entity (and its self link)?

Looks like your projection definition is not even discovered and thus it doesn't get applied if you select it for the HTTP request.
For projection interfaces to be auto-discovered they need to be placed inside the very same or a sub-package of the package of the domain type they're bound to.
If you can't put the type into that location, you can manually register a projection definition on RepositoryRestConfiguration by calling ….projectionConfiguration().addProjection(…).
The reference documentation does not really mention this at the moment but there's already a ticket to get this fixed in future versions.

Related

What is the professional/standard way to save an object with a foreign key?

I am developing a simple dictionary RESTful API with Spring-mvc. There are two related entities:
public class Word {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String word;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="LANGUAGE_ID", insertable=false, updatable=false)
private Language language;
}
Based on the code above the related entity is Language.
Issue: I would like to implement the CREATE operation on the Word entity with POST request. The implementation is not difficult, but I did find at least two solution candidates in relation to the POST request URL and JSON request body:
Solution Candicate I: directly insert with JSON body request. The JSON body contain the nested JSON object - Language, something like
{id:1, word:"hello", Language: {id:1, language:"English"}}
reference: Spring: Save object with foreign keys with a POST request
Solution Candidate II: get the referenced Language id through the POST request URL, say something like
POST http://localhost:8080/rest/language/1/words
As such, there is no Language reference at all in the JSON POST request body.
reference: https://www.youtube.com/watch?v=_Jnu_jHfQbM
I have 2 questions:
Question 1: among these two solution candidates, which is the better one, or say professional standard solution? or is there any other solution?
Question 2: as to both the given solution candidate, in any case we need to retrieve the referenced Language POJO at least in the corresponding controller class. But from the perspective of OO-design principle, this way seems to be tightly coupled with the controller, so I am thinking that should we decouple this retrieval behavior somewhere else than in controller? for instance in the service layer. But is this the professional way? and we need to have a corresponding DTO?
In my opinion the data which should be saved has to be nested in the body. Spring could map the json data directly into an object and you don't have to set it from parameter to another model class.
And i would create separate model classes for your entities. So the controller fills the data to the model classes and give them to a service. Then the service maps the model classes to entities. After that they could be stored via repositories.
Example:
#Controller
public class RestController {
#Autowired
RestService restService;
#PostMapping(value="/")
public void saveVariable( #RequestBody TestModel testModel ) {
testService.saveTest( testModel );
}
}
#Service
public class RestService {
#Autowired
TestRespository testRepository;
public void saveTest( TestModel testModel ) {
TestEntity testEntity = new TestEntity();
//some mapping from testModel to testEntity
testRepository.save( testEntity );
}
}
Assuming that words belong to a language, I would design it as following:
POST /api/languages/en/words HTTP/1.1
Host: localhost:8080
Content-Type: application/json
{
"word": "hello"
}
Where a representation of a word (a JSON document, for example) is sent in the request payload to a URL that represents a hierarchy (a language has words).
You also could use two-letter codes to identify the languages, since it's the way clearer than numeric values. In the above example, en means English.
I advise you to avoid exposing persistence entities in your REST API and use DTOs instead. Your REST resources representations don't need to have the same attributes as the persistence objects.
Keep your REST controllers as lean as possible and keep your service layer focused on business rules.

How can NodaTime be used with EF Code First?

I really want to be able to use NodaTime in my Entity Framework Code First database projects but haven't found a "clean" way to do it. What I really want to do is this:
public class Photoshoot
{
public Guid PhotoshootId{get; set;}
public LocalDate ShootDate{get; set;} //ef ignores this property
}
Is there any supported or recommended approach to using NodaTime with EF Code First?
Until custom primitive type persistence is natively supported in Entity Framework, a common work around is to use buddy properties.
For each custom primitive within your domain model, you create an associated mapped primitive to hold the value in a format supported by Entity Framework. The custom primitive properties are then calculated from the value of their corresponding buddy property.
For example:
public class Photoshoot
{
// mapped
public Guid PhotoshootId{get; set;}
// mapped buddy property to ShootDate
public DateTime ShootDateValue { get; set; }
// non-mapped domain properties
public LocalDate ShootDate
{
get { // calculate from buddy property }
set { // set the buddy property }
}
}
We use NodaTime in our code first POCO's using exactly this approach.
Obviously this leaves you with a single type acting as both a code first POCO and a domain type. This can be improved at the expense of complexity by separating out the different responsibilities into two types and mapping between them. A half-way alternative is to push the domain properties into a subtype and make all mapped buddy properties protected. With a certain amount of wanging Entity Framework can be made to map to protected properties.
This rather splendid blog post evaluates Entity Framework support for various domain modelling constructs including encapsulated primitives. This is where I initially found the concept of buddy properties when setting up our POCO's:
http://lostechies.com/jimmybogard/2014/04/29/domain-modeling-with-entity-framework-scorecard/
A further blog post in that series discusses mapping to protected properties: http://lostechies.com/jimmybogard/2014/05/09/missing-ef-feature-workarounds-encapsulated-collections/
EF Core 2.1 has a new feature Value Conversions, which is exactly for this scenario.
//OnModelCreating
builder.Entity<MyEntity>
.Property(e => e.SomeInstant)
.HasConversion(v => v.ToDateTimeOffset(), v => Instant.FromDateTimeOffset(v));
.HasConversion has some other overloads to make this logic re-useable, for example you can define your own ValueConverter.
No "clean" way that I'm aware of because EF, as of this writing, doesn't have a mechanism for simple type conversion like you see in NHibernate (IUserType). A real limitation in EF as an ORM which causes me to change my domain to suit my ORM.
There is a provider specific way that works with Postgres (Npgsql).
Install the library
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime
And then while configuring DbContext, use this,
services.AddDbContext<PhotoshootDbContext>(opt =>opt.UseNpgsql(Configuration.GetConnectionString("ConnectionString"), o => o.UseNodaTime()));
There are some third party libraries for other providers too.

Combining URL and POST variables in ServiceStack

I am trying to convert an existing wcf rest api to ServiceStack, and having issues right out of the gate:
[Route("foo/{userId}","POST")]
public class MyInputModel : IReturnVoid
{
public string userId { get; set; }
public SomeOtherObject properties { get; set; }
}
The intention here is that I would provide the userId in the url, and an instance of SomeOtherObject in the post body. The error I get is
<Message>Could not deserialize 'application/xml' request using MyInputModel'
Error: System.Runtime.Serialization.SerializationException:
Error in line 1 position 42. Expecting element 'MyInputModel'
from namespace 'blahblahblah'.. Encountered 'Element' with name
'SomeOtherObject', namespace 'http://blahblahblah'.
The only things I can think of are to wrap my xml in a MyInputModel to make the serializer happy. This is not really an option for backwards compatibility.
I could also modify SomeOtherObject to be the top level input model, and put a UserId property in there, but this also feels suboptimal since it is an object used throughout the api, and is really not tied to a user id. It is also already published independently, so it would be painful to make changes there.
Is there any way to indicate that the root element of the posted data will be a SomeOtherObject insted of a MyInputModel? In WebApi this would be with the [FromBody] attributes and whatnot. Does servicestack have anything similar?
The purpose of a DTO is to auto-generate the wire format which is why ServiceStack requires the Request DTO to match the shape of the incoming request. Part of what makes ServiceStack so productive is that it's a code-first web service framework which encourages starting from C# and projecting out, i.e. your clients should bind to your web service outputs and not the other way round of mapping code-first models to existing schema inputs.
Having said that, the Serialization / Deserialization wiki page lists the different ways to override ServiceStack's default request binding with your own.
Access HTTP Request variables in any Service or Filter
Not everything needs to be mapped to a DTO as any HTTP Variable can still be accessed from the IHttpRequest available from any service or filter, i.e:
base.Request.QueryString
base.Request.FormData
base.Request.Headers[name]
base.Request.PathInfo
base.Request.AbsoluteUri

Reusable Querying in Entity Framework WITHOUT Repository. How?

Let me say, I have come to the conclusion (after a lot of trial) that Repository & Unit of Work when using Entity Framework is just wrong, wrong, wrong and this says why quite well.
But I really hate on those embedded queries. Question is, where can I put them instead if I'm so against a repository, etc? (clean answers only please, examples much appreciated).
I just nuked two projects containing my repositories, unit of work and interfaces with hundreds of files because the payback was nowhere to be seen. I think lots of people, myself included, just jumped on the Repository bandwagon because that's what everybody else was doing but in retrospect, I think it's really a ride to nowhere.
/sigh
Richard
Where do you expect to put them? You have only few choices:
Let them be where they are and use custom extension methods, query views, mapped database views or custom defining queries to define reusable parts
Expose every single query as method on some separate class. The method mustn't expose IQueryable and mustn't accept Expression as parameter = whole query logic must be wrapped in the method. But this will make your class covering related methods much like repository (the only one which can be mocked or faked). This implementation is close to implementation used with stored procedures.
You will do the same as in previous method but instead of placing queries in separate class you will put them as static methods to entity directly. This is much worse testable because static methods cannot be replaced by mocking (it requires more complex testing framework). This is part of active record pattern where each entity is responsible for its loading and saving to database.
Example of custom extension method:
public static IQueryable<TEntity> GetByName(this IQueryalbe<TEntity> query, string name)
where TEntity : IEntityWithName
{
return query.Where(e => e.Name == name);
}
Example of custom class exposing methods:
public class QueryProvider
{
public QueryProvider() {}
public IEnumerable<TEntity> GetByName(IYourContext context, string name)
where TEntity : IEntityWithName
{
return context.CreateObjectSet<TEntity>().Where(e => e.Name == name).ToList();
}
}
Build Reusable, Testable Queries Part 1
This is a blog post I wrote about building reusable queries. Using Extension Methods allows you to build composable queries.
using a pattern like the specification pattern can help you build queries that can be reused or saved (serialized). Further more if you have a double entry system you can execute the same query instance over two different databases.
the following example does not use EF but replace the IEnumerable by an EF context and you get what ou are looking for. parameters are passed in through the constructor.
public class PartialMatchQuery : IModelQuery<string, IEnumerable<string>>
{
private readonly string partial;
public PartialMatchQuery(string partialString)
{
partial = partialString;
}
public IEnumerable<string> Execute(IEnumerable<string> model)
{
return model.Where(s => s.ToLower().Contains(partial));
}
}

RIAServices unsupported types on hand-built DomainService

My EF model was generated from my SQL Server database. I then generated a DomainService for RIAServices against the EF model. One of the entities is called "EntryCategories". The DomainService created this method:
public IQueryable<EntryCategories> GetEntryCategoriesSet()
{
return this.Context.EntryCategoriesSet;
}
Since my user interface display model looks quite different from the physical model, I decided to write my own DomainService for that and related entities. Yes, I know we are meant to modify the generated one but it has so much stuff in there and I wanted to focus on a small thing.
I removed the EnableClientAccess attribute from the generated DomainService and added a new class called ClientDomainService, and encapsulated in it the generated DomainService:
[EnableClientAccess()]
public class ClientDomainService : DomainService
{
// the generated domain service encapsulated in my new one.
private DataDomainService _dcds = new DataDomainService();
// reimplement one of the DataDomainService methods
public IQueryable<EntryCategories> GetEntryCategories()
{
return (from t in _dcds.GetEntryCategoriesSet() where t.EntryCategoriesVersions.EntryCategoriesVersionId == datahead.EntryCategoriesVersions.EntryCategoriesVersionId orderby t.DisplayOrder select t);
}
}
The very fist thing I tried is to reimplement the GetCateogoriesSet method but with the underlying data filtered based on another entity in my class (not shown). But when I build this, an error shows up:
Entity 'DataProject.Web.EntryCategories' has a property 'EntryCategoriesVersionsReference' with an unsupported type
If I comment out my CientDomainService, replace the EnableClientAccess attribute on the generated DomainService, and place the analagous linq filtering in the original GetEntryCategoriesSet method, the project compiles with no errors.
What is so special about the generated DomainService that my new one doesn't have? Is it that metadata.cs file?
What's special about the generated domain service is not the .metadata.cs file (you can keep it, and use it, but it doesn't solve your problem).
The problem appears somehow because RIA services (?) needs a 'domain service description provider' for the exposed Linq to EF entities. The LinqToEntitiesDomainService class has the LinqToEntitiesDomainServiceDescriptionProviderAttribute, already applied, so the generated domain services which inherit from it also inherit the provider.
When you build your own custom domain service, derived from DomainService, and expose entities through it, you need to apply this attribute yourself. Furthermore, since the provider cannot infer the object context type from the domain service base class (which it can and does if the base class is LinqToEntitiesDomainService), you need to specify the object context type in the attribute constructor, like this:
[EnableClientAccess()]
[LinqToEntitiesDomainServiceDescriptionProvider(
typeof(YourObjectContextType))]
public class ClientDomainService : DomainService
{
...
}
That should fix it.
Note that this means if you had hoped to abstract your object context away from your domain service, you'll be disappointed. I had opted for the seemingly popular repository model where all code that operates on the object context goes into a provider used by the domain service. This facilitates unit testing, but evidently doesn't remove the domain service's dependency on the object context. The context is required for RIA Services to make sense of your entites, or at least those referenced by the domain entity (such as EntryCategoriesVersions in your case).
If you want to expose a specific entity on a domain service you will have to provde at least one query method for it. This is also required when the entity is only accessed as a child of another entity.
In this case you need to add the EntryCategoriesVersions entityset to the domain service, to get the scenario working correctly.
What type is EntryCategoriesVersionsReference ? Try adding a [DataContract] annotation against the type, and appropriate [Key] and [DataMember]. It should help with marshalling.
For me, the fix for this error was to add a default constructor to the return type.
In OP's example, the property 'EntryCategories.EntryCategoriesVersionsReference' needs to be of a type with a default constructor.