With or without this annotation, there is a property on my JPA #Entity
#Entity
public class Myentity extends ResourceSupport implements Serializable {
...
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="idrepository")
#JsonInclude(JsonInclude.Include.ALWAYS)
private MyentitySource entitysource;
...
}
that is not being mapped when I return:
#RequestMapping("/myentity/{uuid}")
public ResponseEntity<Myentity> getResourceById(#PathVariable("uuid") UUID uuid) {
Myentity result = myentityRepository.findOne(uuid);
return ResponseEntity.ok(myentityAssembler.toResource(result));
}
myentityAssembler.toResource(result) does contain this MyentitySource entitysource, but the JSON output does not.
The weirdest thing is I have another spring boot hateoas project where I am using the exact same entity, repository, controller, and assembler implementations, with the exact same dependencies and versions on my pom, and a very similar configuration (I am not defining any special jackson mappers or anything, just using the default rest/hateoas configuration), and it does work there: The MyentitySource entitysource property, which is another JPA entity extending ResourceSupport, gets serialized and included into the JSON output.
I have been a couple of hours at it already, but I am quite lost. I have verified this behavior is happening all through the application in both applications: #ManyToOne relations defined on any #Entity are being mapped and present in the JSON output on one application, but not in the other.
How can I get these fields to show up on the JSON output?
entitysource will be included if MyentitySource is not an exported entity. If it is one - what seems to be the case here - then it would be wrong to include it. Including associations could lead to sending the whole database to the client. Moreover it is a separate resource with its own URI. Consequently a link to that URI is included in the response.
CascadeType.ALL implies that Myentity is an aggregate, therefore MyentitySource should not be exported in the first place. That would solve your problem. If my assumption is wrong, then you can still use Projections to get entitysource included. I can refer you to this answer from Spring's Oliver Gierke and the relevant chapter of the documentation.
Related
As is the recommended pattern in EF Core these days when you have a many to many join we do something like this:
Fluent API, many-to-many in Entity Framework Core
Having done that I'm faced with the issue as to how I go about exposing that in the OData model.
Since technically the Entity type definition has no key property (as it uses a composite key the OData framework doesn't like me adding the set to the model.
Whats the recommended approach to this problem?
It seems that EF and OData have become somewhat synced up, what would be even better would be if they could literally share model building code.
To that end I found that in the OData model build after calling AddSet I was able to define the key in the same way as I did in EF ...
Builder.EntityType<UserRole>().HasKey(ac => new { ac.UserId, ac.RoleId });
This is somewhat clean, I have not yet tried posting or directly requesting such a type yet, but expanding from either side of the relationship chain seems to work fine.
EDIT: Moredetails on the definition of this in controller ...
The Url needs to contain both key parts ...
HTTP GET ~/UserRole(123, 456)
the same with PUTs and DELETEs but POST's don't contain the key they are part of the object in the body.
The method signature must contain both key parts, here's some examples ...
public IActionResult Get([FromODataUri]int keyUserId, [FromODataUri]Guid keyRoleId)
{
...
}
public IActionResult Put([FromODataUri]int keyUserId, [FromODataUri]Guid keyRoleId)
{
...
}
public IActionResult Post(UserRole entity)
{
...
}
By default, in Spring Data Rest the #Id of the entity is not exposed. In line with the REST rules, we're supposed to use the URI of the resource to refer to it. Given this assumption, the findBy queries should work if you pass a URI to them, but they don't.
For example, say I have a one-to-many relationship between Teacher and Student. I want to find students by teacher.
List<Student> findByTeacher(Teacher teacher)
http://localhost:8080/repositories/students/search/findByTeacher?teacher=http://localhost:8080/repositories/teachers/1
This doesn't work because the framework is attempting to convert the teacher URI to a Long.
I get this error that says "Failed to convert from type java.lang.String to type java.lang.Long".
Am I missing something?
You could expose #Id s by configuring web intializer
//Web intializer
#Configuration
public static class RespositoryConfig extends
RepositoryRestMvcConfiguration {
#Override
protected void configureRepositoryRestConfiguration(
RepositoryRestConfiguration config) {
config.exposeIdsFor(Teacher.class);
}
}
Its good to change List to Page
List findByTeacher(Teacher teacher)
to
Page<Student> findByTeacher(#Param("teacher) Teacher teacher, Pageable pageable);
Also note #Param annotation is required along with Pageable. The latter is required because return type "Page"
3.Latest snapshots, not milestones work fine
See https://jira.spring.io/browse/DATAREST-502
Depending of your version of Spring Data, it would work as you want or not. If you are with Spring Data 2.4, you need to pass the URI. If you are with a previous version, you need to pass the id.
There are several Spring Data projects like Neo4j that use the Spring Data Commons to build up a PersistentEntity/PeristentProperty (basically type info plus property geters and setters) and EntityConverter to roll from a native store to Java. This is what the SDN (Spring Data Neo4j) does plus it bundling BeanWrapper converters to make sure that certain property types are allowed for the Neo4j data structure.
Basically Java beans are stamped with a #NodeEntity annotation and the beans is decomposed on writes into nodes (think a bean with only simple properties) interlinked by relationship objects.
Wondering if I can do the same with Orika? Means identifying classes via an annotation and processing each property when complex recursively. For example:
#NodeEntity
class Software {
String name;
....
Organisation organisation;
....
}
#NodeEntity
class Organisation {
String name;
}
Should be rolled into 2 nodes each containing the property name and a relationship object (denotes Organisation as a member of Software).
Here is an example of an Orika ClassMapBuilder supporting custom annotations, I think you can adapt it to meet your needs.
Gist : AnnotationClassMapBuilder
For Node (or DBObject of MongoDB) you can use a custom property resolver, take a look at:
http://orika-mapper.github.com/orika-docs/advanced-mappings.html (ElementPropertyResolver)
Edit
Orika build mappers by class-map which are actually, just a collection of property-pair, property can be any thing which has name, type and setter or/and getter.
You can automatically create for each attribute in your beans an equivalent in Neo4J side, and let Orika build the mapper.
For example you can create a Person(name)->PrintStream mapper,
in which you create for each person's property (name) an equivalent that print data (System.out)
Example
final Builder name = new Property.Builder()
.name("name")
.type(String.class.getName())
.setter("append(\"My name : \").append(%s).append('\\n')");
factory.classMap(Person.class, PrintStream.class).fieldMap("name", name, false).add().register();
factory.getMapperFacade().map(person, System.out); // This print to default output stream, My name : xxxx
I'm trying to code a class handling serialization of documents by reading their metadata. I got inspired by this implementation for entities with Doctrine ORM and modified it to match how Doctrine ODM handles documents. Unfortunatly something is not working correctly as one document is never serialized more than once even if it is refered a 2nd time thus resulting on incomplete serialization.
For example, it outputs this (in json) for a user1 (see User document) that belongs to some place1 (see Place document). Then it outputs the place and the users belonging to it where we should see the user1 again but we don't :
{
id: "505cac0d6803fa1e15000004",
login: "user1",
places: [
{
id: "505cac0d6803fa1e15000005",
code: "place1",
users: [
{
id: "505c862c6803fa6812000000",
login: "user2"
}
]
}
]
}
I guess it could be related to something preventing circular references but is there a way around it ?
Also, i'm using this in a ZF2 application, would there be a better way to implement this using the ZF2 Serializer ?
Thanks for your help.
I have a serializer already written for DoctrineODM. You can find it in http://github.com/superdweebie/DoctrineExtensions - look in lib/Sds/DoctrineExtensions/Serializer.
If you are are using zf2, then you might also like http://github.com/superdweebie/DoctrineExtensionsModule, which configures DoctrineExtensions for use in zf2.
To use the Module, install it with composer, as you would any other module. Then add the following to your zf2 config:
'sds' => [
'doctrineExtensions' => [
'extensionConfigs' => [
'Sds\DoctrineExtensions\Serializer' => null,
),
),
),
To get the serializer use:
$serializer = $serivceLocator->get('Sds\DoctrineExtensions\Serializer');
To use the serializer:
$array = $serializer->toArray($document)
$json = $serializer->toJson($document)
$document = $serializer->fromArray($array)
$document = $serializer->fromJson($json)
There are also some extra annotations available to control serialization, if you want to use them:
#Sds\Setter - specify a non standard setter for a property
#Sds\Getter - specify a non standard getter fora property
#Sds\Serializer(#Sds\Ignore) - ignore a property when serializing
It's all still a work in progress, so any comments/improvements would be much appreciated. As you come across issues with these libs, just log them on github and they will get addressed promptly.
Finally a note on serializing embedded documents and referenced documents - embedded documents should be serialized with their parent, while referenced documents should not. This reflects the way data is saved in the db. It also means circular references are not a problem.
Update
I've pushed updates to Sds/DoctrineExtensions/Serializer so that it can now handle references properly. The following three (five) methods have been updated:
toArray/toJson
fromArray/fromJson
applySerializeMetadataToArray
The first two are self explainitory - the last is to allow serialization rules to be applied without having to hydrate db results into documents.
By default references will be serialized to an array like this:
[$ref: 'CollectionName/DocumentId']
The $ref style of referencing is what Mongo uses internally, so it seemed appropriate. The format of the reference is given with the expectation it could be used as a URL to a REST API.
The default behaviour can be overridden by defineing an alternative ReferenceSerializer like this:
/**
* #ODM\ReferenceMany(targetDocument="MyTargetDocument")
* #Sds\Serializer(#Sds\ReferenceSerializer('MyAlternativeSerializer'))
*/
protected $myDocumentProperty;
One alternate ReferenceSerializer is already included with the lib. It is the eager serializer - it will serialize references as if they were embedded documents. It can be used like this:
/**
* #ODM\ReferenceMany(targetDocument="MyTargetDocument")
* #Sds\Serializer(#Sds\ReferenceSerializer('Sds\DoctrineExtensions\Serializer\Reference\Eager'))
*/
protected $myDocumentProperty;
Or an alternate shorthand annotation is provided:
/**
* #ODM\ReferenceMany(targetDocument="MyTargetDocument")
* #Sds\Serializer(#Sds\Eager))
*/
protected $myDocumentProperty;
Alternate ReferenceSerializers must implement Sds\DoctrineExtensions\Serializer\Reference\ReferenceSerializerInterface
Also, I cleaned up the ignore annotation, so the following annotations can be added to properties to give more fine grained control of serialization:
#Sds\Serializer(#Sds\Ignore('ignore_when_serializing'))
#Sds\Serializer(#Sds\Ignore('ignore_when_unserializing'))
#Sds\Serializer(#Sds\Ignore('ignore_always'))
#Sds\Serializer(#Sds\Ignore('ignore_never'))
For example, put #Sds\Serializer(#Sds\Ignore('ignore_when_serializing')) on an email property - it means that the email can be sent upto the server for update, but can never be serialized down to the client for security.
And lastly, if you hadn't noticed, sds annotations support inheritance and overriding, so they play nice with complex document structures.
Another very simple, framework independent way to transforming Doctrine ODM Document to Array or JSON - http://ajaxray.com/blog/converting-doctrine-mongodb-document-tojson-or-toarray
This solution gives you a Trait that provides toArray() and toJSON() functions for your ODM Documents. After useing the trait in your Document, you can do -
<?php
// Assuming in a Symfony2 Controller
// If you're not, then make your DocmentManager as you want
$dm = $this->get('doctrine_mongodb')->getManager();
$report = $dm->getRepository('YourCoreBundle:Report')->find($id);
// Will return simple PHP array
$docArray = $report->toArray();
// Will return JSON string
$docJSON = $report->toJSON();
BTW, it will work only on PHP 5.4 and above.
Suppose I have a class Customer that is mapped to the database and everything is a-ok.
Now suppose that I want to retrieve - in my application - the column name that NH knows Customer.FirstName maps to.
How would I do this?
You can access the database field name through NHibernate.Cfg.Configuration:
// cfg is NHibernate.Cfg.Configuration
// You will have to provide the complete namespace for Customer
var persistentClass = cfg.GetClassMapping(typeof(Customer));
var property = persistentClass.GetProperty("FirstName");
var columnIterator = property.ColumnIterator;
The ColumnIterator property returns IEnumerable<NHibernate.Mapping.ISelectable>. In almost all cases properties are mapped to a single column so the column name can be found using property.ColumnInterator.ElementAt(0).Text.
I'm not aware that that's doable.
I believe your best bet would be to use .xml files to do the mapping, package them together with the application and read the contents at runtime. I am not aware of an API which allows you to query hibernate annotations (pardon the Java lingo) at runtime, and that's what you would need.
Update:
Judging by Jamie's solution, NHibernate and Hibernate have different APIs, because the Hibernate org.hibernate.Hibernate class provides no way to access a "configuration" property.