Why can't I use .orElseThrow() method on an Entity? - spring

The following method works.
#GetMapping("/usuarios/{codigo_us}")
EntityModel<Usuario> one(#PathVariable Long codigo_us) { //HATEOAS way of forwarding resources
Usuario usuario = repository.findById(codigo_us)
.orElseThrow(() -> new UsuarioNotFoundException(codigo_us));
// Spring WebMvcLinkBuilder
return assembler.toModel(usuario);
}
But when I try a slightly different approach it doesn't work.
#GetMapping("/usuarios/{cedula_us}")
EntityModel<Usuario> getByCedula(#PathVariable Long cedula_us){
Usuario usuario = repository.findByCedula_us(cedula_us).get(0)
.orElseThrow(() -> new UsuarioNotFoundException(cedula_us));
return assembler.toModel(usuario);

Different classes have different methods available.
The first method returns an Optional<Usuario> which has method orElseThrow()
The second method returns some kind of Iterable/List/Collection, on which you call get(0) which grabs the first element Usuariobut no longer wrapped in Optional. Therefore there is no orElseThrow() available.
You can achieve the same thing for second method by wrapping it into optional:
Usario usario = repository.findByCedula_us(cedula_us).get(0);
usario = Optional.ofNullable(usario).orElseThrow(...
This is just an example to clarify how it works, a better approach would be to add a method in the repository itself to only return first result and return optional, something like:
Optional<Usario> findFirstByCedula_us(String cedula_us)

The return value of findByCedula_us method is not optional it should be like this
Optional<Usario> findByCedula_us(Long cedula_us);

Related

Don't know how to treat that as a predicate

I'm trying to run a custom query in my repository, but I'm getting a InvalidDataAccessResourceUsageException. "Don't know how to treat that as a predicate String("n.id = '1234'")".
public void myMethod() {
myRepository.queryUsingCustomFilters("n.id = '1234'");
}
public interface MyRepository() extends Neo4jRepository<MyObject, String> {
#Query("MATCH (n) WHERE {filter} RETURN n")
List<MyObject> queryUsingCustomFilters(#Param("filter") String filter);
}
I have a simple example for now, but the string I'm passing in the future could be a little bit more complicated, such as "n.id = '1234' AND (n.name = 'one name' OR n.name = 'another name')"
I don't believe you can pass entire clauses/predicates/queries as a #Param.
If you want to build queries at run time, you might want to look at composing it using the lower level Neo4j OGM filters (see https://neo4j.com/docs/ogm-manual/current/reference/#reference:filters)
So in the case you describe above, you could simply add Filters as required and chain them together to build your WHERE clause

ResponseEntity returns null list of objects

I am able to consume an API who returns a json object. What I am trying to do is to get a list of objects instead. Below is my code for the same:
ResponseEntity<List<Entity>> responseEntity = restTemplate.exchange(dataUrl, HttpMethod.GET, entity,new ParameterizedTypeReference<List<Entity>>() {});
where Entity of an object of which I am expecting a list to be populated with data from json. But when I print it, all fields from Entity has null value set. What am I missing?
Thanks
Sach
Can you try with this
ResponseEntity<List<Entity>> responseEntity = restTemplate.exchange(dataUrl, HttpMethod.GET, entity, Entity.class);
p.s. sorry don't have reputation for comment :(
Why don't use restTemplate.getForEntity?
ResponseEntity<Entity[]> response = restTemplate.getForEntity(dataUrl, Entity[].class)
Entity[] entities = response.getBody();
The above case returns an object of type Entity[], which is an array.
If you want to use the List interface, you will need to create a wrapper class.
The wrapper:
public class EntityList {
private List<Entity> entities;
public EntityList() {
entities= new ArrayList<>();
}
// standard constructor and getter/setter
}
The RestTemplate call:
Here you will use restTemplate.getForObject
EntityList response = restTemplate.getForObject(dataUrl, EntityList.class);
List<Entity> entities = response.getEntities();
Even a simpler alternative is to use List.class as the return type of getForObject.
List<Entity> response= rest.getForObject(dataUrl, List.class);
It is hard to give a correct answer due to a missing json example. Could you please provide a sample of the json that will be returned.
RESOLVED
Below is the code change I did which was returning correct list of Entity objects.
ResponseEntity<Entity[]> responseEntity = restTemplate.exchange(dataUrl, HttpMethod.GET, entity, Entity[].class);
Even after making this change didnt really solved my issue. I also had to add
#JsonProperty("fieldName") on each field in Entity class which had to match Json properties.

MapODataRoute and ODataQueryOptions

Im building a WebAPI OData solution that handles untyped entity objects, as described in this excellent post. Like that post, I define my EdmModel upfront, and use the MapODataRoute method and pass in the model to use:
config.Routes.MapODataRoute("odata", "odata", ModelBuilder.GetEdmModel());
However, this does not seem to work with ODataQueryOptions parameter in my methods:
Get(ODataQueryOptions query)
{
}
It gives the following error: The given model does not contain the type 'System.Web.Http.OData.IEdmEntityObject'. Parameter name: elementClrType
Is there any way to get ODataQueryOptions to work with MapODataRoute?
You should build the ODataQueryOptions manually in your controller action in untyped mode. Sample code follows,
ODataPath path = Request.GetODataPath();
IEdmType edmType = path.EdmType;
IEdmType elementType = edmType.TypeKind == EdmTypeKind.Collection
? (edmType as IEdmCollectionType).ElementType.Definition
: edmType;
// build the typeless query options using the element type.
ODataQueryContext queryContext = new ODataQueryContext(Request.GetEdmModel(), elementType);
ODataQueryOptions queryOptions = new ODataQueryOptions(queryContext, Request);
Ive managed to do it as follows:
ODataPath path = Request.GetODataPath();
IEdmType edmType = path.EdmType;
private ODataQueryOptions GetODataQueryOptions(IEdmType edmType)
{
IEdmModel model = Models.ModelBuilder.GetEdmModel();
ODataQueryContext queryContext = new ODataQueryContext(model, edmType);
ODataQueryOptions queryOptions = new ODataQueryOptions(queryContext, this.Request);
return queryOptions;
}
This works for most query options, but crashes with $select and $expand: The type 'Collection([Org.Microsoft.Product Nullable=False])' is not an entity type. Only entity types support $select and $expand. Is there a way to avoid this exception gracefully when a client tries to filter with $select and $expand, or should i just write something like
if (Request.RequestUri.Query.Contains("select")) { return errormessage }
Also, and more importantly, how would one apply these query options to an EdmEntityObjectCollection which gets returned in the first method?
queryOptions.ApplyTo(collectionProduct.AsQueryable()); // wont work...
(perhaps it is better practice to dynamically build your collection in regards to the query options anyway)

Web API parameter filtering

This must be simple and I'm being incredibly dense but I can't find an example to help me figure it out. I want to filter my list of tblAsset items by their assessmentId which is passed in through a parameter. I'm able to get the parameter value ok, but I'm not sure how to write the query.
My model is built from an existing Database using the Model creation wizard.
Thanks for any help!
public IEnumerable<tblAsset> GettblAssets()
{
NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
var assessmentId = nvc["aid"];
//limit the assets by assessmentId somehow and return
}
You could use the .Where extension method on the IQueryable<tblAsset> instance returned by your database:
public IEnumerable<tblAsset> GettblAssets()
{
NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
var assessmentId = nvc["aid"];
// TODO: you might need to adjust the property names of your model accordingly
// Also if the assessmentId property is an integer on your model type
// you will need to parse the value you read from the request to an integer
// using the int.Parse method
return db.tblAsset.Where(a => a.assessmentId == assessmentId);
}

Use of "#this" in Moles delegates

When I set a property of a moled type, it looks like they always require, as the first parameter, an object of the original moled type. I also noticed that some of the examples in the Moles Reference Guide assign this parameter as #this and I am trying to figure out why.
For instance, the original class looks something like this:
public class ProductDAO
{
internal void Insert(Product product, string id)
{
...
}
}
When I go to mole this method, the property is expecting a delegate whose first parameter is always the type of the moled object, in this case, a ProductDAO object. So in this case, the property is expecting a delegate of:
Action<ProductDAO, Product, string>
So do I always have to provide that moled object as the first parameter of my lambda expression? If so, what's the difference in using a regular variable name versus #this? What does #this mean/do?
MProductDAO.AllInstances.InsertProductString = (dao, product, id) => { };
versus
MProductDAO.AllInstances.InsertProductString = (#this, product, id) => { };

Resources