Spring Mongo - set custom collection on entity implementing interfaces - spring

Given a mongo entity class :
#Data
#Document(collection = "#{ T(de.axa.services.ecm.contentmanagement.service.archivbearbeiten.util.ServiceUtils).getMongoCollectionPreFix() }_allgarchiv")
public class AllgArchivReference implements Kleinsparte {
My aim is to store this entity in an collection "xxx_allgarchiv" whereby "xxx" is based on the runtime environment.
I have one Crud repository:
#Repository
public interface ReferenceRepository extends MongoRepository<Kleinsparte, String> {
}
and the simple implementation....
result = referenceRepository.save(kleinsparte);
No I want to store the AllgArchivReference in the collection above ("xxx_allgarchiv").
Unfortunately the entity is stored in a new created collection "kleinsparte" but this is not what i want.
How could I enforce spring data to store allgarchivReference in eponymous collection without creating a new own crud repository?
By the way, there more than 20 other Reference-Entities which are also implementing the interface "Kleinsparte" and i don't want to create for each entity an own crud repository interface.
Is this possible? Any help are really appreciate.
Kind regards,
Bodo

Related

How to avoid Spring Repository<T, ID> to leak persistence information into service tier

I'm using spring-data-mongodb at the moment so this question is primarily in context of MongoDB but I suspect my question applies to repository code in general.
Out of the box when using a MongoRepository<T, ID> interface (or any other Repository<T, ID> descendent) the entity type T is expected to be the document type (the type that defines the document schema).
As a result injecting such a repository into service component means this repository is leaking database schema information into the service tier (highly pseudo) :
class MyModel {
UUID id;
}
#Document
class MyDocument {
#Id
String id;
}
interface MyRepository extends MongoRepository<MyDocument, String> {
}
class MyService {
MyRepository repository;
MyModel getById(UUID id) {
var documentId = convert(id, ...);
var matchingDocument = repository.findById(documentId).orElse(...);
var model = convert(matchignDocument, ...);
return model;
}
}
Whilst ideally I'd want to do this :
class MyModel {
UUID id;
}
#Document
class MyDocument {
#Id
String id;
}
#Configuration
class MyMagicConversionConfig {
...
}
class MyDocumentToModelConverter implements Converter<MyModel, MyDocument> {
...
}
class MyModelToDocumentConverter implements Converter<MyDocument, MyModel> {
...
}
// Note that the model and the model's ID type are used in the repository declaration
interface MyRepository extends MongoRepository<MyModel, UUID> {
}
class MyService {
MyRepository repository;
MyModel getById(UUID id) {
// Repository now returns the model because it was converted upstream
// by the mongo persistence layer.
var matchingModel = repository.findById(documentId).orElse(...);
return matchingModel ;
}
}
Defining this conversion once seems significantly more practical than having to consistently do it throughout your service code so I suspect I'm just missing something.
But of course this requires some way to inform the mongo mapping layer to be aware of what conversion has to be applied to move between MyModel and MyDocument and to use the latter for it's actual source of mapping metadata (e.g. #Document, #Id, etc.).
I've been fiddling with custom converters but I just can't seem to make the MongoDB mapping component do the above.
My two questions are :
Is it currently possible to define custom converters or implement callbacks that allow me to define and implement this model <-> document conversion once and abstract it away from my service tier.
If not, what is the idiomatic way to approach cleaning this up such that the service layer can stay blissfully unaware of how or with what schema an entity is persisted? A lot of Spring Boot codebases appear to be fine with using the type that defines the database schema as their model but that seems supoptimal. Suggestions welcome!
Thanks!
I think you're blowing things a bit out of proportion. The service layer is not aware of the schema. It is aware of the types returned by the repository. How the properties of those are mapped onto the schema, depends on the object-document mapping. This, by default, uses the property name, as that's the most straightforward thing to do. That translation can either be customized using annotations on the document type or by registering a FieldNamingStrategy with Spring Data MongoDB.
Spring Data MongoDB's object-document mapping subsystem provides a lot of customization hooks that allows transforming arbitrary MongoDB documents into entities. The types which the repositories return are your domain objects that - again, only by default - are mapped onto a MongoDB document 1:1, simply because that's the most reasonable thing to do in the first place.
If really in doubt, you can manually implement repository methods individually that allow you to use the MongoTemplate API that allows you to explicitly define the type, the data should be projected into.
You can use something like MapStruct or write your own Singleton Mapper.
Then create default methods in your repository:
interface DogRepository extends MongoRepository<DogDocument, String> {
DogDocument findById(String id);
default DogModel dogById(String id) {
return DogMapper.INSTANCE.toModel(
findById(id)
);
}
}

Spring Boot + MongoDB+create collection

I have a problem. After having created a Spring Boot project with Eclipse and configuring the application.properties file, my collections are not created, whereas after execution, the Eclipse console signals that the connection to MongoDB has been carried out normally. I don't understand what's going on. With MySQL we had the tables created so I expected the creation of the collections, but nothing.
Summary, I don't see my collection (class annoted #Document) in MongoDB after deployment.
New collection won't be created until you insert at least one document. Refer the document Create Collection
You could do this in two ways through Spring. I tested the instructions below in Spring 2.1.7.
With just a #Document class, Spring will not create a collection in Mongo. It will however create a collection if you do the following:
You have a field you want to index in the collection, and you annotate it as such in the Java class. E.g.
#Indexed(unique = true)
private String indexedData;
Create a repository for the collection:
public interface MyClassRepository extends MongoRepository<MyClass, String> {
}
If you don't need/want an index, the second way of doing this would be to add some code that runs at startup, adds a dummy value in the collection and deletes it again.
#Configuration
public class LoadDatabase {
#Bean
CommandLineRunner initDb(MyClassRepository repository) {
// create an instance of your #Document annotated class
MyClass myDocument = new MyClass();
myDocument = repository.insert(myDocument);
repository.delete(myDocument);
}
}
Make sure your document class has a field of the correct type (String by default), annotated with #Id, to map Mongo's _id field.

how to retrieve objects when using inheritance in spring Data

Say I have a class structure as follows, it is pretty basic inheritance:
Manager extends Person {
private String name;
Manager() {
}
}
Clerk extends Person {
private String salary;
}
In spring Data if I store these in Mongo, is it possible to configure it to map the correct class when I do a getById. I assume i will have to store some class info?
What i dont want to do is the need to create seperate repository classes if i can avoid it, also i dont know what the object will be when i do a getById
If you are using spring-data-mongodb MongoRepository to write data in your database according to your entity model, a _class field will be added to document roots and to complex property types (see this section). This fields store the fully qualified name of the Java class and it allows disambiguation when mapping from MongoDb Document to Spring data model.
However, if you only use MongoRepository to read from your database, you need to tell Spring-data how to map your entities explicitly. You will need to Override Mapping with Explicit Converters.
PersonReadConverter.class
public class PersonReadConverter implements Converter<Document, Person> {
#Override
public Contact convert(Document source) {
if (source.get("attribute_specific_to_Clerk") != null) {
Clerk clerk = new Clerk();
//Set attributes using setters or defined constructor
return clerk;
}
else {
Manager manager = new Manager()
//Set attribute using setters or defined constructor
return manager;
}
}
}
Then, you have to Register Spring Converters with the MongoConverter.
You can find an example of my own at: Spring Data Mongo - How to map inherited POJO entities?

filter dynamodb from list in springboot

I have a Spring boot application using an AWS DynamoDb table which contains a list of items as such:
#DynamoDBTable(tableName = MemberDbo.TABLENAME)
public class MemberDbo {
public static final String TABLENAME = "Member";
#NonNull
#DynamoDBHashKey
#DynamoDBAutoGeneratedKey
protected String id;
// some more parameters
#DynamoDBAttribute
private List<String> membergroupIds;
}
I would like to find all members belonging to one specific groupId. In best case I would like to use CrudRepository like this:
#EnableScan
public interface MemberRepository extends CrudRepository<MemberDbo, String> {
List<MemberDbo> findByMembergroupIdsContaining(String membergroupIds); // actually I want to filter by ONE groupId
}
Unfortunately the query above is not working (java.lang.String cannot be cast to java.util.List)
Any suggestions how to build a correct query with CrudRepository?
Any suggestions how to create a query with Amazon SDK or some other Springboot-compliant methods?
Alternatively can I create a dynamoDb index somehow and filter by that index?
Or do I need to create and maintain a new table programmatically containing the mapping between membergroupIds and members (which results in a lot of overhead in code and costs)?
A solution for CrudRepository is preferred since I may use Paging in future versions and CrudRepository easily supports paging.
If I have understood correctly this looks very easy. You using DynamoDBMapper for model persistence.
You have a member object, which contains a list of membergroupids, and all you want to do is retrieve this from the database. If so, using DynamoDBMapper you would do something like this:
AmazonDynamoDB dynamoDBClient = new AmazonDynamoDBClient();
DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient);
MemberDbo member = mapper.load(MemberDbo.class, hashKey, rangeKey);
member.getMembergroupIds();
Where you need to replace hashKey and rangeKey. You can omit rangeKey if you don't have one.
DynamoDBMapper also supports paging out of the box.
DynamoDBMapper is an excellent model persistence tool, it has strong features, its simple to use and because its written by AWS, it has seamless integration with DynamoDB. Its creators have also clearly been influenced by spring. In short, I would use DynamoDBMapper for model persistence and Spring Boot for model-controller stuff.

MVC (Spring): Logic Organisation Entity and Service

I have a simple question about code organisation.
TeamRepository (Repository Class)
TeamService (Service Class)
Team (Entity Class)
I want to set a new team leader for an team object. Should this function "setLeader(User user)" in the Entity Class or better in the TeamService Class?
team.setLeader(user);
teamService.save(team);
or
teamService.setLeader(team, user);
Inside the "setLeader" Function there is a if condition (if user is a team member) then declare the user as the new team leader.
I guess it's ok in the entity class or? I am confused because i have read that "business logic" should not be in entity classes.
Best regards
Kindly keep in mind that:
Entity Class:
In a good designed architecture, it should be just like a POJO class, containing simple attributes, default constructor, their getter() & setters() and other optional methods like toString() & equal() etc.
Example:
#Entity
public Class Team{
private String id;
private User leader;
public Team(){}
//getters & setters
}
Repository Class:
This main purpose of this class should be either save, update, delete or retrieve simple record from a database, no business logic.
Service Class:
This is the class where you can specify all business/ domain related logic here. It should populate required entity object, and then call specific repository class to save given entity.
#Service
public class TeamService{
#Autowired
TeamRepository repo;
public void createTeam(){
Team team = new Team();
if(//user is the member of team){
team.setLeader(user);//assume user object is already initialized
}
repo.save(user);
}
}
Since leader is a attribute of your Team-class, leader should be directly manipulated with the setter-method of your Team-class. That's my opinion/feeling.
There is no higher logic or computation behind the action. It's just setting the value of the entity's attribute.

Resources