use of GeoJsonPoint with mongoDB and spring data - mongodb

I tried hard to build an example to save my enity like below:
Resource class has ResourceDetail in which I defined GeoJsonPoint.
#Document(collection = "Resource")
public class Resource implements Serializable {
/**
*
*/
private static final Long serialVersionUID = 1L;
#Id
private Long id;
private String name;
private Integer age;
private String orgName;
private String resourceType;
private String department;
private boolean showPrice;
private Integer price;
private ResourceDetail resourceDetail;
}
#Document(collection = "ResourceDetail")
public class ResourceDetail implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String identityNumber;
#DateTimeFormat(iso = ISO.DATE)
private Date birthDate;
private String addressLine1;
private String addressLine2;
private String specialization;
private GeoJsonPoint location;
}
I have added following mapper also in Appconfig:
#Configuration
#ComponentScan(basePackages = "com.test")
#EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
#Bean
public MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(new MongoClient(), "test");
}
#Bean
public MongoTemplate mongoTemplate() throws Exception {
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory());
return mongoTemplate;
}
/**
* Read JSON data from disk and insert those stores.
*
* #return
*/
public #Bean ObjectMapper repositoryPopulator() {
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(GeoJsonPoint.class, GeoJsonPointMixin.class);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
}
static abstract class GeoJsonPointMixin {
GeoJsonPointMixin(#JsonProperty("longitude") double x, #JsonProperty("latitude") double y) {
}
}
}
I am getting this error:
{
"timestamp": 1445857673601,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.http.converter.HttpMessageNotReadableException",
"message": "Could not read JSON: No suitable constructor found for type [simple type, class org.springframework.data.mongodb.core.geo.GeoJsonPoint]: can not instantiate from JSON object (need to add/enable type information?)\n at [Source: java.io.PushbackInputStream#d7eff8; line: 13, column: 25] (through reference chain: com.appointment.domain.Resource[\"resourceDetail\"]->com.appointment.domain.ResourceDetail[\"location\"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class org.springframework.data.mongodb.core.geo.GeoJsonPoint]: can not instantiate from JSON object (need to add/enable type information?)\n at [Source: java.io.PushbackInputStream#d7eff8; line: 13, column: 25] (through reference chain: com.appointment.domain.Resource[\"resourceDetail\"]->com.appointment.domain.ResourceDetail[\"location\"])",
"path": "/rest/resource/add"
}
I used this format to save my enity with GeoJsonPoint:
{
"name":"Test",
"age": 32,
"orgName":"testOrg",
"resourceType":"testresourcType",
"price":1200,
"department":"testDepartment",
"resourceDetail": {
"identityNumber": "3",
"birthDate": "2000-10-10",
"location" : { "latitude":40.743827, "longitude":-73.989015 }
}
Please help me to solve this. Thanks

Probably late, but you need to use GeoJSON format in your mongo.
"location" : {
"type" : "Point",
"coordinates" : [
-2.6637,
54.6944
]
}
be aware that coordinates are in this order : longitude, latitude
See more :
http://geojson.org/
http://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/core/geo/GeoJson.html

Tested your Mixin code an it worked. Although, I would suggest you to make sure you are sending application/json as content type and the json structure is correct (In your example there is a missing }).
There is a similar question like yours and this issue can be solved by registering a GeoJsonModule as well: https://stackoverflow.com/a/37340077/3697851

Related

Eclipselink translatedsqlstring exception

I have a simple JPA entity with #AdditionalCriteria mentioned for the login language. I also have specified a query redirector for this class. When I attempt to get the translated sql string in the query redirector, I get a null pointer exception. The reason is that the field in the entity is called lang and the additional criteria parameter is LOGIN_LANGUAGE. The exception is thrown when the line 273 of class org.eclipse.persistence.internal.expressions.ParameterExpression is executed.
My JPA entity looks like this
#QueryRedirectors(allQueries=VPMQueryRedirector.class)
#AdditionalCriteria(value = "this.lang = :LOGIN_LANGUAGE")
public class AuthorityTextView extends EntityCommons implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "AUTHORITYID", length = 36)
private String authorityId;
#Id
#Column(name = "LANG", length = 2)
private String lang;
#Column(name = "AUTHORITYTEXT", length = 255)
private String authorityText;
#Column(name = "DEFAULTUSED")
private Boolean defaultUsed;
public String getAuthorityId() {
return authorityId;
}
public String getLang() {
return lang;
}
public String getAuthorityText() {
return this.authorityText;
}
public Boolean getDefaultUsed() {
return this.defaultUsed;
}
}
My Query Redirector is listed below
public class VPMQueryRedirector implements QueryRedirector {
private static final long serialVersionUID = 3912645701055442481L;
private Logger logger = LoggerFactory.getLogger(getClass());
#Override
public Object invokeQuery(DatabaseQuery query, Record arguments, Session session) {
query.setDoNotRedirect(true);
String translatedSQLString = query.getTranslatedSQLString(session, arguments);
}
I have create a bug under eclipselink, but there hasn't been any updates yet if the observation is correct or not.

Picketlink with custom model and long Id

I have a existing Model and want to use it with Picketlink. But I am using Long as #Id field. But Picketlink expect this to be a String field. I have found some hints to use another entity which maps to the corresponding entity of my model. But actually I don't now how to do it.
I have a base class, which all entities derive from:
#MappedSuperclass
public abstract class AbstractEntity implements Serializable, Cloneable {
#Id
#Identifier
#Column(name = "SID")
private Long sid;
#Column(name = "INSERT_TIME")
private Date insertTime;
#Column(name = "UPDATE_TIME")
private Date updateTime;
// getters and setters
}
And a derived realm entity:
#Entity
#IdentityManaged(Realm.class)
public class RealmEntity extends AbstractEntity {
#AttributeValue
private String name;
#PartitionClass
private String typeName;
#ConfigurationName
private String configurationName;
#AttributeValue
private boolean enforceSSL;
#AttributeValue
private int numberFailedLoginAttempts;
// getters and setters
}
And the mapping class for Picketlink looks as follows:
#IdentityPartition(supportedTypes = {
Application.class,
User.class,
Role.class
})
public class Realm extends AbstractPartition {
#AttributeProperty
private boolean enforceSSL;
#AttributeProperty
private int numberFailedLoginAttempts;
private Realm() {
this(null);
}
public Realm(String name) {
super(name);
}
}
The PartitionManager is defined as follows:
builder
.named("default.config")
.stores()
.jpa()
.supportType(User.class, Role.class, Application.class, Realm.class)
.supportGlobalRelationship(Grant.class, ApplicationAccess.class)
.mappedEntity(App.class, AppUserRole.class, AppRole.class, AppUser.class, UserEntity.class, RelationshipIdentityTypeEntity.class, RealmEntity.class)
.addContextInitializer((context, store) -> {
if (store instanceof JPAIdentityStore) {
if (!context.isParameterSet(JPAIdentityStore.INVOCATION_CTX_ENTITY_MANAGER)) {
context.setParameter(JPAIdentityStore.INVOCATION_CTX_ENTITY_MANAGER, entityManager);
}
}
});
When I try to create a new Realm Hibernate throws an error while trying to load the Realm because the #Id is defined as Long but the #Identifier of the Picketlink model is a String.
this.shsRealm = new Realm(REALM_SHS_NAME);
this.shsRealm.setEnforceSSL(true);
this.shsRealm.setNumberFailedLoginAttempts(3);
this.partitionManager.add(this.shsRealm);
java.lang.IllegalArgumentException: Provided id of the wrong type for class de.logsolut.common.picketlink.model.RealmEntity. Expected: class java.lang.Long, got class java.lang.String
How can I map the JPA model correctly to Picketlink?

Mapstruct: how to map multiple fields from DTO to an object in Entity?

i have this DTO:
#NoArgsConstructor
public class DataDTO implements DTO {
private static final long serialVersionUID = -5105904799152965475L;
private Long deviceId;
private OffsetDateTime generatedOn;
public Long getDeviceId() {
return deviceId;
}
public void setDeviceId(Long deviceId) {
this.deviceId = deviceId;
}
public OffsetDateTime getGeneratedOn() {
return generatedOn;
}
public void setGeneratedOn(OffsetDateTime generatedOn) {
this.generatedOn = generatedOn;
}
}
i have this MongoDB document:
#Document(collection = "data")
#EqualsAndHashCode
public class DataDocument {
private static final long serialVersionUID = 1772572723546311500L;
#Id
private IdByDeviceIdAndGeneratedOn id;
public DataDocument() {
}
public IdByDeviceIdAndGeneratedOn getId() {
return id;
}
public void setId(IdByDeviceIdAndGeneratedOn id) {
this.id = id;
}
}
and this is the #Id class for MongoDB Document:
#EqualsAndHashCode
#ToString
public class IdByDeviceIdAndGeneratedOn {
#Id
private final Long deviceId;
#Id
#Field("generated_on")
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private final OffsetDateTime generatedOn;
public IdByDeviceIdAndGeneratedOn(final Long deviceId, final OffsetDateTime generatedOn) {
this.deviceId = Objects.requireNonNull(deviceId);
this.generatedOn = Objects.requireNonNull(generatedOn);
}
public Long getDeviceId() {
return deviceId;
}
public OffsetDateTime getGeneratedOn() {
return generatedOn;
}
}
this is the mapper for this Key class:
#Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR, componentModel = "spring")
public interface IdByDeviceIdAndGeneratedOnMapper {
default IdByDeviceIdAndGeneratedOn toId(final Long deviceId, final OffsetDateTime generatedOn) {
return new IdByDeviceIdAndGeneratedOn(deviceId, generatedOn);
}
default Long getDeviceId(final IdByDeviceIdAndGeneratedOn id) {
return id.getDeviceId();
}
default OffsetDateTime getGeneratedOn(final IdByDeviceIdAndGeneratedOn id) {
return id.getGeneratedOn();
}
and this is the #Mapper for DataDTO and DataDocument:
#Mapper( unmappedTargetPolicy = ReportingPolicy.ERROR,
uses = {IdByDeviceIdAndGeneratedOnMapper.class,
AccelerometerDocumentMapper.class,
GpsDocumentMapper.class,
GsmDocumentMapper.class
})
public interface DataDocumentMapper extends DocumentMapper<DataDTO, DataDocument> {
}
this is the generic mapper:
/**
* Contract for a generic dto to entity mapper.
*
* #param <DTO> - DTO source type parameter.
* #param <DOCUMENT> - MongoDB Document destination type parameter.
*/
public interface DocumentMapper<DTO, DOCUMENT> {
DOCUMENT toDocument(DTO dto);
DTO toDto(DOCUMENT document);
}
Currently i'm receiving this errors:
for MongoDB Data docment:
Unmapped target property: "id".
for DTO:
Unmapped target properties: "deviceId, generatedOn".
How to solve this errors without loosing immutability of Id class?
What you are trying to do is to use (using constructors to construct objects) is not yet supported. There is an open issue for it #73.
However, you can achieve what you are looking for by using Object factories, this is for the toDocument mapping, for the toDto mapping you can use nested source mappings.
Your mapper would look like:
#Mapper(uses = {AccelerometerDocumentMapper.class,
GpsDocumentMapper.class,
GsmDocumentMapper.class},
componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface DataDocumentMapper extends DocumentMapper<DataDTO, DataDocument> {
#Mapping(target = "id", source = "dto")
#Override
DataDocument toDocument(DataDTO dto);
#ObjectFactory
default IdByDeviceIdAndGeneratedOn createId(DataDTO dto) {
return dto == null ? null : new IdByDeviceIdAndGeneratedOn(dto.getDeviceId(), dto.getGeneratedOn());
}
#Mapping(target = "deviceId", source = "id.deviceId")
#Mapping(target = "generatedOn", source = "id.generatedOn")
#Override
DataDTO toDto(DataDocument document);
}
NB: You can also make DataDocumentMapper abstract class and make the createId method protected, in case you don't want to expose it in the interface
this is solved my problem, but this doesnt look elegant.
Maybe there is more elegant way?
#Mapper(uses = {AccelerometerDocumentMapper.class,
GpsDocumentMapper.class,
GsmDocumentMapper.class},
imports = {IdByDeviceIdAndGeneratedOn.class},
componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface DataDocumentMapper extends DocumentMapper<DataDTO, DataDocument> {
#Override
#Mapping(target = "id", expression = "java( new IdByDeviceIdAndGeneratedOn(dto.getDeviceId(), dto.getGeneratedOn()) )")
DataDocument toDocument(DataDTO dto);
#Override
#Mapping(target = "deviceId", expression = "java( document.getId().getDeviceId() )")
#Mapping(target = "generatedOn", expression = "java( document.getId().getGeneratedOn() )")
DataDTO toDto(DataDocument document);
}

Issue with projection in SpringDataRest and #Lob attribute

I have an Entity Person :
#Entity
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = AUTO, generator = "PERSON_SEQ")
private Integer idPerson;
private String lastName;
private String firstName;
#Lob
private byte[] picture;
A repository
public interface PersonRepository extends PagingAndSortingRepository<Person, Integer> {}
A projection
#Projection(name = "picture", types = { Person.class })
public interface ProjectionPicturePerson {
byte[] getPicture();
}
When i used the projection : ..../persons/1?projection=picture
i have this error
There was an unexpected error (type=Internal Server Error, status=500).
Could not write content: [B cannot be cast to [Ljava.lang.Object; (through reference chain: org.springframework.data.rest.webmvc.json.["content"]->$Proxy109["picture"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: [B cannot be cast to [Ljava.lang.Object; (through reference chain: org.springframework.data.rest.webmvc.json.["content"]->$Proxy109["picture"])
When i use a projection on a String, exemple lastName it works
#Projection(name = "lastName", types = { Person.class })
public interface ProjectionLastName {
String getLastName();
}
When i don't use projection it works too
jackson serialize the image attribute
Is there a restriction on Blob ?
That's a bug in ProxyProjectionFactory. I've filed and fixed DATACMNS-722 for you scheduled to be in the upcoming services releases (mid next week).

Spring Data MongoDB

I'm new to Spring Data MongoDB.
I'm trying to use #DBRef to refer a Document (Comments) from another Document (News) with spring-data-mongodb-1.7.0.RELEASE.jar.
To make things easy, I'm not passing anything as parameters for the rest call. The Comments collection exist in the database. I'm getting the following exception:
SEVERE: Servlet.service() for servlet [appServlet] in context with
path [/news] threw exception [Handler processing failed; nested
exception is java.lang.NoSuchMethodError:
com.mongodb.DBRef.(Ljava/lang/String;Ljava/lang/Object;)V] with
root cause java.lang.NoSuchMethodError:
com.mongodb.DBRef.(Ljava/lang/String;Ljava/lang/Object;)V
The following is my code:
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document
public class Comments{
#Id
private String id;
private String Comment;
public Comments(String comment){
this.id = UUID.randomUUID().toString();
this.comment = comment
}
//getters & setters
}
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
#Document
public class News{
#Id
private String id;
private String Title;
private String summary;
#DBRef
private List<Comments> comments;
public News(){
}
public News(String title, String summary){
this.id = UUID.randomUUID().toString();
this.title = title;
this.summary = summary;
}
//getters and setters
}
#RestController
#RequestMapping(value = "/rest/test")
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
#RequestMapping(value = "/createnews", method = RequestMethod.POST)
public void createNews() {
logger.info("createNews method started");
News news = new News("News Title", "News summary");
List<Comments> commentList = new ArrayList<Comments>();
Comments comments = new Comments("News Comments");
commentList.add( comments );
news.setComments( commentList );
logger.info("createNews method ended");
}
}
Problem:
I am unable to use #DBRef with spring-data-mongodb-1.7.0.RELEASE.jar.
I'm looking for advice from the experts. Thanks in advance.
Just Delete the "#DBRef".
You can't use DBRef to link a document to the list.
Spring data mongodb would transfer the List of your domain class as a child array in the document(As Embedded Documents).
Like this:
{
_id: 100,
Title: "food",
summary: "xyz",
Comments: [ { id: "111", Comment: "shipping" }, { id: "111", Comment: "shipping" } ]
}
See http://docs.mongodb.org/manual/tutorial/query-documents/#read-operations-embedded-documents for more details.
How are you saving your entities. You will first have to save the referred entity and then the parent entity.
News news = new News("News Title", "News summary");
Comments comments = new Comments("News Comments");
news.setComments( Arrays.asList(comments) );
mongoTemplate.save(comments);
mongoTemplate.save(news);