I have class along the lines of:
public class Person {
#TextIndexed
String name;
List<Person> contacts;
// getters and setters
...
}
The #TextIndexed allows me to search people by name. However, after I recently added a list of contacts I discovered that their names will also be added to the text index. Is there a way around this other than having a separate class - identical to Person but without the #TextIndexed annotations? Perhaps an annotation that will serialize the field but not utilize its indexes?
I am not aware of such an annotation, but one possible way to go about this would be to use polymorphism. For example, an abstract Person class that is extended by both versions (indexed and non-indexed). Then only annotate the name attribute inside the class where you want it indexed.
Related
I am trying to come up with a way of implementing tags for my entity that works well for me and need some help in the process. Let me write down some requirements I have in mind:
Firstly, I would like tags to show in entities as a list of strings like this:
{
"tags": ["foo", "bar"]
}
Secondly, I need to be able to retrieve a set of available tags across all entities so that users can easily choose from existing tags.
The 2nd requirement could be achieved by creating a Tag entity with the value of the Tag as the #Id. But that would make the tags property in my entity a relation that requires an extra GET operation to fetch. I could work with a getter method that resolves all the Tags and returns only a list of strings, but I see two disadvantages in that: 1. The representation as a list of strings suggests you could store tags by POSTing them in that way which is not the case. 2. The process of creating an entity requires to create all the Tags via a /tags endpoint first. That seem rather complicated for such a simple thing.
Also, I think I read somewhere that you shouldn't create a repository for an entity that isn't standalone. Would I create a Tag and only a Tag at any point in time? Nope.
I could store the tags as an #ElementCollection in my entity. In this case I don't know how to fulfill the 2nd requirement, though.
#ElementCollection
private Set<String> tags;
I made a simple test via EntityManager but it looks like I cannot query things that are not an #Entity in a result set.
#RestController
#RequestMapping("/tagList")
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class TagListController implements RepresentationModelProcessor<RepositoryLinksResource> {
#PersistenceContext
private final #NonNull EntityManager entityManager;
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<EntityModel<TagList>> get() {
System.out.println(entityManager.createQuery("SELECT t.tags FROM Training t").getFirstResult());
EntityModel<TagList> model = EntityModel.of(new TagList(Set.of("foo", "bar")));
model.add(linkTo(methodOn(TagListController.class).get()).withSelfRel());
return ResponseEntity.ok(model);
}
}
org.hibernate.QueryException: not an entity
Does anyone know a smart way?
The representation as a list of strings suggests you could store tags by POSTing them in that way which is not the case
This is precisely the issue with using entities as REST resource representations. They work fine until it turns out the internal representation (entity) does not match the external representation (the missing DTO).
However, it would probably make most sense performance-wise to simply use an #ElementCollection like you mentioned, because you then don't have the double join with a join table for the many-to-many association (you could also use a one-to-many association where the parent entity and the tag value are both part of the #Id to avoid a join table, but I'm not sure it's convenient to work with. Probably better to just put a UNIQUE(parent_id, TAG) constraint on the collection table, if you need it). Regarding the not an entity error, you would need to use a native query. Assuming that you have #ElementCollection #CollectionTable(name = "TAGS") #Column(name = "TAG") on tags, then SELECT DISTINCT(TAG) FROM TAGS should do the job.
(as a side note, the DISTINCT part of the query will surely introduce some performance penalty, but I would assume the result of that query is a good candidate for caching)
I have a model like the one below (assume as pseudo code )
class Student {
#Id
private String id;
private String firstname;
.....;
#DBRef(lazy=true)
private College college
// getters and setters
}
class College {
#Id
private String id;
private String name;
// other attributes.
// getters and setters
}
I am using #DBRef(lazy=true) so that I do not load the college associated with the student. For example: if I have a repository method for Student called findByFirstname(String firstname), I can load the student without the college.
However, at times I would also want to load the student with college. Is it possible to write a repository method with a custom query using the #Query annotation (org.springframework.data.mongodb.core.query.Query) where I can load the student (all fields) and also the associated college instance ?
#Query( what should go here ?)
Student findStudentWithCollege(String firstname)
If no, then what would be a suggested way to load lazy documents on demand ?
As per the documentation
"DBRefs can also be resolved lazily. In this case the actual Object or Collection of references is resolved on first access of the property. Use the lazy attribute of #DBRef to specify this. Required properties that are also defined as lazy loading DBRef and used as constructor arguments are also decorated with the lazy loading proxy making sure to put as little pressure on the database and network as possible." I guess this may not be suitable for cases where one would want to load a student whose last name is "Smith" along with the college instance for each of the students retrieved.
I have a MongoRepository class
public interface UserRepository extends MongoRepository<User, Long> {
User findById(Long id);
}
and my Entity pojo looks like this
#Document(collection = "user")
class User {
Long id;
String name;
Department department;
…
}
When I call the findBy method, a User object is returned. I want to know how does Spring Data MongoDB converts DBObject to Java object. I was under the impression that Spring Data MongoDB uses some sort of mapper (Jackson?) under the hood which would call setters/constructors method of the java(Entity) class based on the field names in the class or #Field Annotation. But to my surprise, the setters are never invoked. Only the default constructor is invoked.
Then how does the fields are set? The reason I am asking is if the setters are called, it would give me an option to set some other fields may be.
Thanks
Spring Data defaults to field access as accessor methods can contain additional logic that we don't want to trigger by accident. If that's what you actually want though, you can switch to property access by annotating your class with #AccessType(Type.PROPERTY).
Spring has a entity converter at the subsequent layer below it. It uses reflection to read the type of field, variables and signature. The conversion logic is generic for all data repositories. You can read about the same here
You can also introduce a custom converter be it yours or jackson, an example of it is here
Take a look at MappingMongoConverter class - it has the logic which does all this.
I am using Spring Data Commons - 1.4.0.RC1 and Spring Data MongoDB - 1.1.0.RC1, having a problem with updating a list of embedded documents(of the same class hierarchy) using mongoTemplate.findAndModify(), which does not set the "_class" value as mongoTemplate.save() does, causing problems when reading data from mongodb as the application does not know which concrete class to instantiate for each embedded doc in that list.
For instance, I have a Student class, and a student has enrolled on subjects such as Maths, Physics, Computer Science, etc. This is not the domain I am working with, but should be enough to demonstrate the problem. So I will have a Student class mapped to a "student" collection and a list of subjects as embedded documents and all subjects extends the Subject super class, as below :
#Document
public class Student {
#Id
private String identifier;
private List<Subject> subjcts;
}
public abstract class Subject {
// common properties and methods;
}
public class Maths extends Subject {}
public class Physics extends Subject{}
To create a Student and save to database, I do
Student student = new Student();
student.setIdentifier(UUID.random().toString);
student.addSubject(new Maths());
student.addSubject(new Physics());
mongoTemplate.insert(student); or mongoTemplate.save(student);
The above will create a student with subjects maths and physics as embedded documents with an extra field "_class" specifying the concrete class so that it can be mapped to the correct class while fetching from the db.
However, if now i want to update the list of subjects, it is intuitive to do something like mongoTemplate.findAndModify(the query, new Update().set("subject", newSubjects), Student.class);
but findAndModify does not seem to go through the type mapper which sets the "_class" for the subjects, hence causes problems in reading back from db.
Is it a bug in Spring Data ? How do I work around it?
Any pointer will be appreciated.
Thank you.
This is a known issue of SpringData MongoDB (https://jira.springsource.org/browse/DATAMONGO-392).
For now the only workaround I found is to create a specific converter for the inner-document class.
I suspect there's no perfect solution to this problem so least worst solution are more than welcome.
I'm implementing a dashboard using PrimeFaces and I would like to persist the model backing it (using JPA2). I've written my own implementation of DashboardModel and DashboardColumn with the necessary annotations and other fields I need. The model is shown below:
#Entity
public class DashboardSettings implements DashboardModel, Serializable{
#Id
private long id;
#OrderColumn( name="COLUMN_ORDER" )
private List<DashboardColumn> columns;
...a few other fields...
public DashboardSettings() {}
#Override
public void addColumn(DashboardColumn column) {
this.columns.add(column);
}
#Override
public List<DashboardColumn> getColumns() {
return columns;
}
...snip...
}
The problem is the columns field. I would like this field to be persisted into it's own table but because DashboardColumn is an interface (and from a third party so can't be changed) the field currently gets stored in a blob. If I change the type of the columns field to my own implementation (DashboardColumnSettings) which is marked with #Entity the addColumn method would cease to work correctly - it would have to do a type check and cast.
The type check and cast is not the end of the world as this code will only be consumed by our development team but it is a trip hazard. Is there any way to have the columns field persisted while at the same time leaving it as a DashboardColumn?
You can try to use targetEntity attribute, though I'm note sure it would be better than explicit cast:
#OrderColumn( name="COLUMN_ORDER" )
#OneToMany(targetEntity = DashboardColumnSettings.class)
private List<DashboardColumn> columns;
Depends on the JPA implementation (you don't mention which one); the JPA spec doesn't define support for interface fields, nor for Collections of interfaces. DataNucleus JPA certainly allows it, primarily because we support it for JDO also, being something that is part of the JDO spec.