Implementing findOne in Spring Data (MongoDB) - java

I have some problems with executing findOne method of MongoOperations class, for now this method return null. My data structure in mongoDB is looking like this:
> db.news.find({_id:1})
{ "_id" : 1, "title" : "first title", "text" : "first text" }
> db.news.find({_id:{$type:1}})
{ "_id" : 1, "title" : "first title", "text" : "first text" }
As you can see above _id field has Double type. My Java classes is looking like this:
#Repository
public class NewsService {
#Autowired
private MongoOperations mongoOperations;
public static final String COLLECTION_NAME = "news";
//this method executes ok
public List<NewsEntity> getAllNews() {
return mongoOperations.findAll(NewsEntity.class, COLLECTION_NAME);
}
//but this method return null
public NewsEntity getNewsDetail(Long id) {
return mongoOperations.findOne(Query.query(Criteria.where("_id").is(id)), NewsEntity.class);
}
Entity class:
#Document
public class NewsEntity {
#Id
private Long id;
private String title;
private String text;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
And Spring controller:
#Controller
public class MainController {
#Autowired
private NewsService newsService;
#RequestMapping(value="/news/details/{newsId}",method = RequestMethod.GET)
public String getNewsDetails(ModelMap model, #PathVariable("newsId") Long newsId) {
//newsEnt is null here...
NewsEntity newsEnt = newsService.getNewsDetail(newsId);
model.addAttribute("newsDet", newsEnt);
return "newsdetails";
}
}

You are calling the mongoOperations instance directly and not first retrieving a collection. So much like the findAll method you have implemented you also need the form that contains the collection as an argument:
public NewsEntity getNewsDetail(Long id) {
return mongoOperations.findOne(
Query.query(Criteria.where("_id").is(id)),
NewsEntity.class,
COLLECTION_NAME
);
}
This is covered in the documentation for findOne, also see the available method signatures in the summary.

Related

UnrecognizedPropertyException: Unrecognized field "sections"

I'm using Jackson as part of a spring boot app. I am turning JSON into Java, and I am getting this error. I did some research, but I still don't understand what is going wrong or how to fix it.
Here is the JSON fragment:
"dataBlock": {
"sections": [
{
"info": "",
"prompt": "",
"name": "First Section",
"sequence": 0,
"fields": [],
"gatingConditions": [],
"guid": "480d160c-c34f-4022-97b0-e8a1f28c49ae",
"id": -2
}
],
"prompt": "",
"id": -1,
"name": ""
}
So my Java object for this "dataBlock" element:
public class DataBlockObject {
private int id;
private String prompt;
private String name;
private List<SectionObject> sections;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPrompt() {
return prompt;
}
public void setPrompt(String prompt) {
this.prompt = prompt;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<SectionObject> getSections() {
return sections;
}
public void setSections(List<SectionObject> sections) {
this.sections = sections;
}
}
And the Section object is this:
public class SectionObject {
private int id;
private String name;
private String prompt;
private String info;
private int sequence;
private List<FieldObject> fields;
private List<GatingConditionObject> gatingConditions;
private String guid;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrompt() {
return prompt;
}
public void setPrompt(String prompt) {
this.prompt = prompt;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public int getSequence() {
return sequence;
}
public void setSequence(int sequence) {
this.sequence = sequence;
}
public List<FieldObject> getFields() {
return fields;
}
public void setFields(List<FieldObject> fields) {
this.fields = fields;
}
public List<GatingConditionObject> getGatingConditions() {
return gatingConditions;
}
public void setGatingConditions(List<GatingConditionObject> gatingConditions) {
this.gatingConditions = gatingConditions;
}
public String getGuid() {
return guid;
}
public void setGuid(String guid) {
this.guid = guid;
}
}
So it seems to me that Jackson would make a DataBlockObject, map the obvious elemenets, and create an array that I have clearly marked as a List named sections. -- just like the JSON shows.
Now the error is:
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "sections" (class com.gridunity.workflow.bean.json.SectionObject), not marked as ignorable (8 known properties: "gatingConditions", "sequence", "prompt", "fields", "id", "info", "guid", "name"])
Now according to that error it would seem that one of my 8 elements should be named "sections" - But that's not one of my elements. It clearly has a problem with my List of Sections, but I cant figure out what it is.
Can someone explain WHY this is happening, especially sence it looks like I have my structure correct, and how to fix this. I have seen this on other posts:
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
But that seems incredibly wrong as I know all of my properties.
It looks like the JSON itself has another sections field in one or more of the dataBlock.sections items. If you don't have control over the construction of the JSON object, you'll need to add a #JsonIgnoreProperties annotation on the SectionObject class so that when the JSON object has fields that aren't specified in the POJO, it won't throw an error during deserialization.
#JsonIgnoreProperties(ignoreUnknown = true)
public class SectionObject {
// class members and methods here
}

Complex Json to Nested POJO spring MVC

I am trying to get the following Json into POJOS using #RequestBody Instance instance
{
"service_id": "service-id-here",
"plan_id": "plan-id-here",
"context": {
"platform": "cloudfoundry",
"some_field": "some-contextual-data"
},
"organization_guid": "org-guid-here",
"space_guid": "space-guid-here",
"parameters": {
"agent_name": 1,
"url": "foo",
"credential": "asdasd",
"ia_url": "asdasd"
}
}
Below are my POJOs
Instance
public class Instance {
#JsonProperty(value = "service_id")
String serviceId;
#JsonProperty(value = "plan_id")
String planId;
//TODO : Replace with Context class when the spec defines things clearly
#JsonProperty(value = "context")
Object context;
#JsonProperty(value = "organization_guid")
String organizationGuid;
#JsonProperty(value = "space_guid")
String spaceGuid;
#JsonProperty(value = "parameters")
Parameters parameters;
}
Parameters
public class Parameters {
#JsonProperty(value = "agent_name")
String agentName;
#JsonProperty(value = "url")
String url;
#JsonProperty(value = "credential")
String credential;
#JsonProperty(value = "ia_url")
String iaUrl;
}
I use #JsonProperty everywhere. Is there any way to get underscore separated json keys into java's naming convention for variables (Camelcase)??
I tried using #JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) to my POJO classes instead of the #JsonProperty for each parameter. I just get an empty json {} in instance. What am I missing here?
Yes, is this possible using PropertyNamingStrategy class through JsonNaming annotation
Ex:
#JsonNaming(PropertyNamingStartergy.LowerCaseWithUnderscoresStrategy.class)
class Class_name{
...
}
//----
The below code has updated. In that code am using
PropertyNamingStrategy.SnakeCaseStrategy
Working code (TESTED).
Getters and setters are important for this to work. But #JsonProperty does not require them
User.java
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class User {
private int id;
private String beanName;
private Role role;
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
Role.java
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class Role {
private int id;
private String roleName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
Here is the Controller
#RestController
#RequestMapping("/test")
public class NamingController {
#RequestMapping(value="/jsontopojo", method = RequestMethod.POST)
public ResponseEntity<User> jsontopojo(#RequestBody User nam) {
return new ResponseEntity<User>( nam, HttpStatus.OK);
}
}

Parsing nested JSON containing both Objects and single values

I have what I believe is called nested JSON and I want to use Jackson to deserialize into objects. Is it possible to automatically parse the child objects into Java Objects as well if a Program class had for example objects of the type TrackedEntity and ProgramStage (see JSON code) ? Alternatively would it be possible to simply parse the "id" of the respective objects and put them in Strings in the Program objects?
JSON Example is as follows:
{
programs:
[
{
"id": "IpHINAT79UW",
"created": "2013-03-04T10:41:07.494+0000",
"trackedEntity":
{
"id": "cyl5vuJ5ETQ",
"name": "Person"
},
"programStages":
[
{
"id": "A03MvHHogjR",
},
{
"id": "ZzYYXq4EJie",
},
{
"id": "AREMvHHogjR",
},
{
"id": "ZzYYXq4fJie",
}
]
},
{
"id": "IGRINAT79UW",
"created": "2013-03-04T10:41:07.494+0000",
"trackedEntity":
{
"id": "cyl5vuJ5ETQ",
"name": "Person"
},
"programStages":
[
{
"id": "A03MvHHogjR",
},
{
"id": "ZzYYXq4fJie",
},
{
"id": "A01MvHHogjR",
},
{
"id": "ZzGYXq4fJie",
}
]
}
]
}
One approach is simply to create POJOs for the various entities.
If you assume the following for TrackEntity
class TrackedEntity {
private final String id;
private final String name;
#JsonCreator
TrackedEntity(
#JsonProperty("id") final String id,
#JsonProperty("name") final String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}
Then the following may be suitable for ProgramStage:
class ProgramStage {
private final String id;
#JsonCreator
ProgramStage(#JsonProperty("id") final String id) {
this.id = id;
}
public String getId() {
return id;
}
}
The Program class is slightly trickier since it must parse som kind of zoned date. I have used the Java 8 ZonedDateTime in this example with a custom formatter. You can also use JSR 310 module as described in this answer.
class Program {
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSxx");
private final ZonedDateTime created;
private final String id;
private final List<ProgramStage> programStages;
private final TrackedEntity trackedEntity;
#JsonCreator
public static Program of(
#JsonProperty("id") final String id,
#JsonProperty("created") final String created,
#JsonProperty("trackedEntity") final TrackedEntity trackedEntity,
#JsonProperty("programStages") final List<ProgramStage> programStages) {
return new Program(
id,
ZonedDateTime.parse(created, FORMATTER),
trackedEntity,
programStages);
}
public Program(
final String id,
final ZonedDateTime created,
final TrackedEntity trackedEntity,
final List<ProgramStage> programStages) {
this.id = id;
this.created = created;
this.trackedEntity = trackedEntity;
this.programStages = programStages;
}
public ZonedDateTime getCreated() {
return created;
}
public String getId() {
return id;
}
public List<ProgramStage> getProgramStages() {
return programStages;
}
public TrackedEntity getTrackedEntity() {
return trackedEntity;
}
}
Finally, to fix the outer programs entity the following can be used:
class Programs {
private final List<Program> programs;
#JsonCreator
Programs(#JsonProperty("programs") final List<Program> programs) {
this.programs = programs;
}
public List<Program> getPrograms() {
return programs;
}
}
To use the whole thing, simply instantiate an ObjectMapper and use the readValue method like this:
final Programs programs = new ObjectMapper().readValue(json, Programs.class);
Yes. You should be fine. Crate a data structure which represents your data:
public class Container
{
public List<ProgramInfo> programs {get;set;}
}
public class ProgramInfo
{
public string id{get; set;}
public DateTime created{get;set;}
public TrackEntity trrack{get;set;}
}
public class TrackEntity
{
public string id{get;set;}
public string name{get;set;}
}
//Then call the deserialise or serialize
Container container = new JavaScriptSerializer().Deserialize<Container>(yourString);
public class TrackedEntity
{
public string id { get; set; }
public string name { get; set; }
}
public class ProgramStage
{
public string id { get; set; }
}
public class Program
{
public string id { get; set; }
public string created { get; set; }
public TrackedEntity trackedEntity { get; set; }
public List<ProgramStage> programStages { get; set; }
}
public class RootObject
{
public List<Program> programs { get; set; }
}
//Then call the deserialise or serialize
var container = new JavaScriptSerializer().Deserialize<RootObject>(inputjson);
hope it works for you.

Why is #PostConstruct causing my variable to be Null in JSF?

I am generating a List when you first load the JSF page. It is a #PostConstruct method.
However, It is causing my variable "Title" in my "FilmResultBean" to be null and I do not understand why.
Relevant Code:
DataTableBean:
#Component
#ManagedBean(name= DataTableBean.BEAN_NAME)
#Scope("request")
public class DataTableBean implements Serializable {
public static final String BEAN_NAME = "dataTableBean";
public static Logger logger = Logger.getLogger(DataTableBean.class);
#Autowired
#ManagedProperty(value = "filmResultBean")
FilmResultBean resultBean;
#Autowired
FilmBo filmBo;
private List<FilmResultBean> filmList;
private List<String> categoryList;
private String categoryName;
private String titleInput;
/* Default Constructor */
public DataTableBean() {
}
/*Queries the Database for a list of Film Objects and returns the films in
* a list of type "FilmResultBean"
*/
#PostConstruct
public void init(){
filmList = filmBo.generateFilmList(resultBean);
}
public void searchByBoth(){
filmList = filmBo.searchByBoth(resultBean);
logger.info("FilmList Inside SEARCHBYBOTH: " + filmList);
}
public String getTitleInput() {
return titleInput;
}
public void setTitleInput(String titleInput) {
this.titleInput = titleInput;
}
public List<String> getCategoryList() {
return categoryList;
}
public void setCategoryList(List<String> categoryList) {
this.categoryList = categoryList;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
public void searchFilms(){
}
public List<FilmResultBean> getFilmList() {
return filmList;
}
public void setFilmList(List<FilmResultBean> filmList) {
this.filmList = filmList;
}
}
JSF Page:(The value is what is coming back as Null,Only when I use #PostConstruct)
<ace:textEntry value="#{filmResultBean.title}">
<ace:ajax listener="#{dataTableBean.searchByBoth}" execute="#this" render="form" />
</ace:textEntry>
Specific Method being called by #PostConstruct:
#Override
public List<FilmResultBean> generateFilmList(FilmResultBean films) {
Session session = getCurrentSession();
List<FilmResultBean> filmList;
Film film = new Film();
Query query = null;
films.setTitle(film.getTitle());
films.setRating(film.getRating());
films.setRentalRate(film.getRentalRate());
String hql;
hql = "from Film";
query = session.createQuery(hql);
logger.info("Query: " + query);
filmList = (query.list());
return filmList;
}
FilmResultBean:
#Component
#ManagedBean
#SessionScoped
#Scope("session")
public class FilmResultBean implements Serializable {
BigDecimal rentalRate;
Short length;
String rating;
String title;
String category;
public FilmResultBean() {
}
public FilmResultBean(BigDecimal rentalRate, Short length, String rating,
String title, String category) {
this.rentalRate = rentalRate;
this.length = length;
this.rating = rating;
this.title = title;
this.category = category;
}
public BigDecimal getRentalRate() {
return rentalRate;
}
public void setRentalRate(BigDecimal rentalRate) {
this.rentalRate = rentalRate;
}
public Short getLength() {
return length;
}
public void setLength(Short length) {
this.length = length;
}
public String getRating() {
return rating;
}
public void setRating(String rating) {
this.rating = rating;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
#Override
public String toString() {
return title;
}
}
Logger Info :
23:51:41,013 INFO om.cooksys.training.dao.impl.FilmDaoImpl: 82 - Title: null
23:51:41,013 INFO om.cooksys.training.dao.impl.FilmDaoImpl: 83 - Category: null
23:51:41,038 INFO om.cooksys.training.dao.impl.FilmDaoImpl: 92 - Query: QueryImpl(select new com.cooksys.training.FilmResultBean(fc.film.rentalRate, fc.film.length, fc.film.rating, fc.film.title, fc.category.name) from Film f join f.filmCategories fc where f.title like :title)
Hibernate:
select
film2_.rental_rate as col_0_0_,
film2_.length as col_1_0_,
film2_.rating as col_2_0_,
film2_.title as col_3_0_,
category6_.name as col_4_0_
from
sakila.film film0_
inner join
sakila.film_category filmcatego1_
on film0_.film_id=filmcatego1_.film_id,
sakila.film film2_,
sakila.category category6_
where
filmcatego1_.film_id=film2_.film_id
and filmcatego1_.category_id=category6_.category_id
and (
film0_.title like ?
)
23:51:41,045 INFO com.cooksys.training.DataTableBean: 63 - FilmList Inside SEARCHBYBOTH: []
It seems that your variable (and others like Category) are null because the injection of the managed bean as property is not correctly performed, you've to specefiy its value by JSF EL expression: #ManagedProperty("#{filmResultBean}").
I think it's not recommended to mix Spring with JSF annotations for your beans and java classes, just keep one of them.
Within the method below, you're trying to set films's properties by empty film instance. The method parameter films is found unused then in the method, what's its utility ?
#Override
public List<FilmResultBean> generateFilmList(FilmResultBean films) {
...
Film film = new Film();
...
films.setTitle(film.getTitle());
films.setRating(film.getRating());
films.setRentalRate(film.getRentalRate());
...
}

Strange behaviour of MongoDB when querying by foreign key

I am writing some test code to learn spring-data with MongoDB. I can successfully create two Documents: Person and ADocument, where ADocument contains a reference to Person.
#Document
public class Person {
#Id
private ObjectId id;
#Indexed
private String name;
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
...
#Document
public class ADocument {
#Id
private ObjectId id;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
private String title;
private String text;
#DBRef
private Person docperson;
public Person getDocperson() {
return docperson;
}
public void setDocperson(Person docperson) {
this.docperson = docperson;
}
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
}
The problem arises when I try to get all the 'adocuments' related to a person by using the person's ID (once the person's name is provided):
public List<ADocument> loadDocumentsByPersonName(String pname) {
Query qPerson = new Query().addCriteria(Criteria.where("name").is(pname));
qPerson.fields().include("_id");
Person pers = mongoTemplate.findOne(qPerson, Person.class);
ObjectId persId = pers.getId();
Query qDoc = new Query().addCriteria(Criteria.where("person.$id").is(persId));
System.out.println(qDoc.toString());
List<ADocument> list2 = mongoTemplate.find(qDoc, ADocument.class);
return list2;
}
Everyting works fine except that list2 is always empty (while it shouldn't).
System.out.println(qDoc.toString()) gives something like:
Query: { "person.$id" : { "$oid" : "536a0d50e4b0d0c10297f2ab"}}, Fields: null, Sort: null
If I try to issue the query above on the Mongo shell I get the following:
db.adocument.find({ "person.$id" : { "$oid" : "536a0805e4b0af174d0b5871"}})
error: {
"$err" : "Can't canonicalize query: BadValue unknown operator: $oid",
"code" : 17287
}
While if I type
db.adocument.find({ "person.$id" : ObjectId("536a0805e4b0af174d0b5871")})
I actually get a result!
I am using MongoDB 2.6.0 and Spring Data 1.4.2.
I really can't figure out what's going on... Any help is extremely appreciated!
I got it!
For some reason, I had to explicit the collection name in the Query:
List list2 = mongoTemplate.find(qDoc, ADocument.class, COLLECTION_NAME);
where COLLECTION_NAME="adocument".
As for the shell behaviour, it seems that Query.toString() does never return a correct syntax to be cut and paste for shell execution.

Categories

Resources