JPA 2 Criteria Query Projection - jpa-2.1

Using a JPA2 Criteria Query we can project the final result on a DTO (for instance) like this:
query.select(builder.construct( ProductGridDTO.class,
root.get(Productos_.proId),
root.get(Productos_.proAlias),
root.get(Productos_.proNombre),
companies.get(Companias_.ciaNombre),
companies.get(Companias_.ciaId)));
However this method is dependant of the order of arguments in my DTO class, which is plain wrong. Using the old (now deprecated) hibernate criteria API we could use the projection list:
ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.property("id"), "id");
projectionList.add(Projections.property("name"), "name");
Which is not dependent of the parameter order of the DTO.
Is it possible to use a similar strategy in JPA?

I don't think so, the argument order must match in JPA. This could be because, until Java 8, parameter names were only available if the bytecode includes debug information. My guess is that most JPA providers end up calling Constructor.newInstance() and here the argument must be ordered correctly.

I moved on to QueryDSL which provides a higher level abstraction and solves this projection issue. So a query to load an entity and return a DTO for said entity becomes:
public List<CompanyDTO> findByCompanyId(String companyId) {
JPAQuery<?> query = new JPAQuery<Void>(em);
QCompany company = QCompany.company;
return query.from(company)
.where(company.companyId.eq(companyId))
.select(Projections.constructor(CompanyDTO.class, company))
.fetch();
}
Where CompanyDTO contains a constructor for the company Entity.

Related

Linq to entity enum not comparing

Here is my Enum:
public enum AdvertStatus
{
Active,
Archived
}
And my entity type:
public record Advertisement
{
...
public AdvertStatus Status { get; set; }
...
}
In database it's stored as int, Database is Postgree
When I try to compare it like so:
data = data.Where(x => x.Status == searchValues.Status);
Entity Framework throws an exception sayng:
.Status == (int)__searchValues_Status_3)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'.
I tried solutions from this question: LINQ TO ENTITY cannot compare to enumeration types but it did't work.
EDIT 1:
data is database table context IQueryable<AdvertisementDTO>
searchValues.Status is type of AdvertStatus from search filter
The issue may be higher up in your Linq query, such as you are attempting to project with a Select or ProjectTo before filtering. For simple types like int/string this should work, but depending on how your DTO is declared you might be introducing problems for mpgsql.
For instance if your query is something like:
var query = _context.Advertisements
.Select(x => new AdvertisementDTO
{
// populate DTO
}).Where(x => x.Status == searchValues.Status)
// ....
then npgsql may be having issues attempting to resolve the types between what is in the DTO and the enumeration in your searchValues. From what the exception detail looks like, npgsql is trying to be "safe" with the enum and casting to intbut feeding that to PostgreSQL that results in invalid SQL. I did some quick checks and the DTO would need to be using the same Enum type (C# complains if the DTO cast the value to int, cannot use == between AdvertStatus and int fortunately) The project may have something like a value converter or other hook trying to translate enumerations which is getting brought into the mix and gunking up the works.
Try performing the Where conditions prior to projection:
var query = _context.Advertisements
.Where(x => x.Status == searchValues.Status)
.Select(x => new AdvertisementDTO
{
// populate DTO
})
// ....
If the data value is stored as an Int then this should work out of the box. npgsql does support mapping to string (which would require a ValueConverter) as well as database declared enumerations. (https://www.postgresql.org/docs/current/datatype-enum.html) However, Int columns should work fine /w enums.
If that doesn't work, I'd try with a new DbContext instance pointed at the DB and a simple entity with that Enum to load a row from that table to eliminate whether npgsql is translating the enum correctly, just to eliminate any possible converters or other code that the main DbContext/models/DTOs may be contributing.
It was all my mistake in higher repo Select projection.
Thanks you all for help. Cheers.

Spring Data Neo4j 4: Bug when updating property to null?

I use Spring Data Neo4j 4 GraphRepository to save and retrieve data. Using GraphRepository save() and findAll() methods.
When I update an existing entity property to null, it seems that changes are not reflected in the returned data.
If I update the property to any other non-null value, the changes are reflected correctly.
I can see that the null property update is performed on the DB server. But the findAll() method doesn't reflect the change and keeps the old value.
Is this a known bug? Any workaround? Or is it some kind of caching problem?
UPDATE
After trying to understand what happens, I found that this problem will occur when you have two different Java objects for the same entity. The null property will never be updated (but other properties with non-null values will).
Example code:
#Autowired
MovieRepository repository;
public void test() {
repository.deleteAll();
Movie movie1 = new Movie();
movie1.setName("Pulp Fiction");
movie1.setDirector("Quentin Tarantino");
movie1 = repository.save(movie1);
System.out.println("Movie1: " + movie1);
Movie movie2 = new Movie();
movie2.setId(movie1.getId());
movie2.setName(movie1.getName());
movie2.setDirector(null); // implicit...
movie2 = repository.save(movie2);
System.out.println("Movie2: " + movie2);
Movie movie3 = repository.findOne(movie1.getId());
System.out.println("Movie3: " + movie3);
}
Real life case: when using SDN with a Spring MVC form, it looks like entities are created from Model attributes. When a value is set to null in a form, the update is performed correctly in Neo4j, but the values are not returned correctly when using any find...() methods. Therefore it leads to stale data.
Side note: this problem happens when the Neo4J session scope is per "session" and doesn't happen when the session scope is per "request".
#Bean
#Override
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
return super.getSession();
}
If you are using HttpSession-scoped persistence in SDN, you should ensure the the objects bound to your Controller via #ModelAttribute have the same scope as the persistence layer. Use the #SessionAttribute annotation on the Controller to achieve this.
If you use HttpRequest-scoped objects in your Controller and HttpSession-scoped persistence, you will get different objects representing the same graph entity at the web layer, and this will confuse the persistence mechanism.
Should not be a problem at all. I just tried
#Test
public void shouldPersistNulls() {
TempMovie movie = new TempMovie( "Pulp Fiction" );
tempMovieRepository.save( movie );
assertSameGraph( getDatabase(), "CREATE (m:Movie {name:'Pulp Fiction'})");
TempMovie loadedMovie = tempMovieRepository.findAll().iterator().next();
loadedMovie.setName(null);
tempMovieRepository.save(loadedMovie);
assertSameGraph( getDatabase(), "CREATE (m:Movie)");
TempMovie loadedAgainMovie = tempMovieRepository.findAll().iterator().next();
assertNull(loadedAgainMovie.getName());
}
and it passed.
Update based on edited question
The property representing the #GraphId must never be set manually i.e. via your code. You should load the entity by id when you require to update it. This ensures that the entity is known to the mapping context of the OGM and is managed correctly.

how to hide metadata in web api 2, odata

I have defined odata route using MapODataServiceRoute in my WebApiConfig.
config.Routes.MapODataServiceRoute("CompanyoOdata", "odata", GetImplicitEdm(config));
private static IEdmModel GetImplicitEdm(HttpConfiguration config)
{
ODataModelBuilder builder = new ODataConventionModelBuilder(config, true);
builder.EntitySet<Company>("Company");
builder.EntitySet<Photo>("Photos");
builder.EntitySet<Country>("Country");
return builder.GetEdmModel();
}
The data service works just fine. But I want to achieve few things.
I don't want to expose my metadata or associations because i'm using it internally and will not need metadata. How can I restrict access to these information (i.e restrict access to http://www.sample.com/odata/#metadata or http://www.sample.com/odata/$metadata)
secondly, I want to ignore some properties from getting serialized. I found two ways of doing this.
Using data contracts and marking properties with [DataMember] attribute or [IgnoreDataMember] attribute
Using Ignore method on EntitySet when building the model
I can't use the first method as I'm using Database first approach for entity framework hence can't decorate the entity with attributes. I thought I can achieve this by using MetaDataType, but it seems it only works for DataAnnotations.
I used second method with success, but you can't pass more than one property in the ignore method. Has to do it to individual property that I need to ignore, which is a bit tedious. Is there another way to do this?
any help really appreciated.
If want to hide metadata (/$metadata) or service document (/), can remove the the MetadataRoutingConvention from existing routing conventions, e.g.:
var defaultConventions = ODataRoutingConventions.CreateDefault();
var conventions = defaultConventions.Except(
defaultConventions.OfType<MetadataRoutingConvention>());
var route = config.MapODataServiceRoute(
"odata",
"odata",
model,
pathHandler: new DefaultODataPathHandler(),
routingConventions: conventions);
If only expose a few properties per type, can use ODataModelBuilder instead of ODataConventionModelBuilder. E.g., some example:
ODataModelBuilder builder = new ODataModelBuilder();
EntityTypeConfiguration<Customer> customer = builder.EntitySet<Customer>("Customers").EntityType;
customer.HasKey(c => c.Id);
customer.Property(c => c.Name);

LINQ and web service cannot return anonymous types, and you cannot construct an object in a query?

Web services cannot return an anonymous type.
If you are building a LINQ query using classes through a datacontext... you cannot construct instances of those classes in a query.
Why would I want to do this? Say I want to join three "tables" or sets of objects. I have three items with a foreign key to each other. And say the lowest, most detailed of these was represented by a class that had fields from the other two to represent the data from those. In my LINQ query I would want to return a list of the lowest, most detailed class. This is one way I have decided to "join some tables together" and return data from each of them via LINQ to SQL via a WebService. This may be bad practice. I certainly do not like adding the additional properties to the lowest level class.
Consider something like this... (please ignore the naming conventions, they are driven by internal consideration) also for some reason I need to instantiate an anonymous type for the join... I don't know why that is... if I do not do it this way I get an error...
from su in _dataContext.GetTable<StateUpdate>()
join sfs in _dataContext.GetTable<SystemFacetState>()
on new { su.lngSystemFacetState } equals new { lngSystemFacetState = sfs.lngSystemFacetState }
join sf in _dataContext.GetTable<SystemFacet>()
on new { sfs.lngSystemFacet } equals new { lngSystemFacet = sf.lngSystemFacet }
join s in _dataContext.GetTable<System>()
on new { sf.lngSystem } equals new {lngSystem = s.lngSystem}
select new
{
lngStateUpdate = su.lngStateUpdate,
strSystemFacet = sf.strSystemFacet,
strSystemFacetState = sfs.strSystemFacetState,
dtmStateUpdate = su.dtmStateUpdate,
dtmEndTime = su.dtmEndTime,
lngDuration = su.lngDuration,
strSystem = s.strSystem
}
).ToList();
Notice I have to build the anonymous type which is composed of pieces of each type. Then I have to do something like this... (convert it to a known type for transport via the web service)
result = new List<StateUpdate>(from a in qr select(new StateUpdate
{
lngStateUpdate = a.lngStateUpdate,
strSystemFacet = a.strSystemFacet,
strSystemFacetState = a.strSystemFacetState,
dtmStateUpdate = a.dtmStateUpdate,
dtmEndTime = a.dtmEndTime,
lngDuration = a.lngDuration,
strSystem = a.strSystem
}));
It is just awful. And perhaps I have created an awful mess. If I am way way off track here please guide me to the light. I feel I am missing something fundamental here when I am adding all these "unmapped" properties to the StateUpdate class.
I hope someone can see what I am doing here so I can get a better way to do it.
You can create a 'dto' class which just contains the properties you need to return and populate it instead of the anonymous object:
public class Result
{
public string lngStateUpdate
{
get;
set;
}
... // other properties
}
then use it like this:
from su in _dataContext.GetTable<StateUpdate>()
...
select new Result
{
lngStateUpdate = su.lngStateUpdate,
... // other properties
}
Nitpick note - please ditch the Hungarian notation and camel casing for properties :)
I think the answer is to create another object to serve as a DTO. This object would not be mapped to the data context and can contain fields that cross the mapped objects. This solves the problems of repetitive properties in the mapped objects, and allows for instantiation of the DTO class in the query as it is not mapped.
FYI: with respect to the problem with the join- I revisited that and I think I may have had the inner and outer components of the join switched around before.

Breeze: How can I create a GUID key for new entities on the client?

Using Breeze, what is the simplest way to populate a GUID key when an entity is created?
I'll assume that your entity is configured such that the client is responsible for setting the Guid key for new entities. That's the default for the Guid key of an Entity Framework Code First entity; it is as if the key property were adorned with [DatabaseGenerated(DatabaseGeneratedOption.None)]
The obvious approach is to set the key after creating the entity and before adding it to the manager, e.g.:
function createFoo() {
var foo = fooType.createEntity();
foo.id(breeze.core.getUuid()); // Knockout implementation
manager.addEntity(foo);
}
This may be all you ever need.
On the other hand, you may find that you're creating new Foos in many places and for some strange reason you can't use the createFoo function. You certainly don't want to repeat that code.
You can extend the Foo entity type with id-setting behavior after which you'd be able to write:
function createFoo() {
var foo = fooType.createEntity(); // foo.id is set for you
manager.addEntity(foo);
}
There are two approaches to consider - custom constructor and type initializer; both are described in "Extending Entities"
Constructor
You can initialize the key inside a custom constructor. Breeze calls the constructor both when you create the entity and when it materializes a queried entity. Breeze will replace the initial key value when materializing.
Here's an example that assumes the Knockout model library.
function Foo() {
foo.id(breeze.core.getUuid()); // using KO
}
// one way to get the MetadataStore
var store = manager.metadataStore;
// register the ctor with the Foo type
store.registerEntityTypeCtor("Foo", Foo);
Pretty simple. The only downside is that Breeze will generate a Guid every time it makes an entity, whether creating a new one or materializing one from a query. It's wasted effort during materialization but so what? Well, I suppose that might become a performance issue although I wouldn't assume so until I had measured it.
Initializer
Suppose you measured and the repeated Guid generation is a serious problem (really?). You could set the key in a type initializer instead and only call the Guid generator when creating a new entity.
Breeze calls a type initializer after the entity has been created or materialized from query just before returning that entity to the application. Clearly you don't want to overwrite a materialized key from the database so you'll test the key value to make sure it's not real (i.e. to make sure you're fixing a created entity) before assigning it. Here's an example.
function fooInitializer(foo) {
var emptyGuid = "00000000-0000-0000-0000-000000000000";
if (foo.id() !=== emptyGuid) {
foo.id(breeze.core.getUuid());
}
}
var store = manager.metadataStore;
// register the initializer; no ctor in this example
store.registerEntityTypeCtor("Foo", function(){}, fooInitializer);
Assuming you have a Guid surrogate Key on all your entities like we have in our case, you could code a createInstance factory that does the following in a very generic approach:
function createInstance(breezeEntityManager, typeName) {
var keyProperty = breezeEntityManager.metadataStore.getEntityType(typeName, false).dataProperties.filter(function (p) {
return p.isPartOfKey;
})[0];
var config = {};
config[keyProperty.name] = breeze.core.getUuid();
return breezeEntityManager.createEntity(typeName, config);
}
This way, you won't have to create an initializer for all your entities.

Resources