Does Spring Data REST support adding custom behavior to single repositories with Spring Data MongoDB? - spring-data-mongodb

Does Spring Data REST support adding custom behavior to single repositories with Spring Data MongoDB and exposing it as a rest resource?
For example, here is the pojo:
#Document
#Data
#EqualsAndHashCode
public class Poet {
#Id
private String id;
private String title;
private String author;
private String content;
}
And Here is the repository:
#CrossOrigin
#RepositoryRestResource
public interface PoetRepository extends MongoRepository<Poet, String>, MyPoetRepository {
}
Can I find all the distinct title attribute in the document poet by extending a custom interface? By the way, how to implement the custom interface?

Related

Multiple Database Type Support for an Entity in SpringBoot

Team
I'm building a spring boot application that can support multiple DBs either Cassandra, CouchDB or DynamoDB based on the configuration in application.yml.
My entity class has annotations that are specific to Cassandra and the annotations for DynamoDB are different. For eg. DynamoDB has #DynamoDBTable for Table and Cassandra has #org.springframework.data.cassandra.core.mapping.Table annotations.
The problem is that I would like to use a single entity object irrespective of the DB type because the entity is referred from multiple places in the application. What is the best design pattern to implement this?
In case of Cassandra -
package com.abc;
#Table("Cart")
public class Cart {
#PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED)
#GeneratedValue(strategy = GenerationType.AUTO)
protected String id;
#PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED)
private String userId;
#PrimaryKeyColumn(ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING)
private String skuId;
In case of DynamoDB -
#DynamoDBTable(tableName = "Cart")
public class Cart {
#DynamoDBHashKey
#DynamoDBAutoGeneratedKey
protected String id;
private String userId;
private String skuId;
Thanks
AA
I would suggest you to create an intermediary object which can act as a bridge between your application logic and database ORM.
You can create a helper function which populate those fields.
class CartDAO {
private String id;
private String userId;
private String skuId;
// Getters & Setters
}
class CartService{
CartDAO fetchFromDynamoDB(String Id)
{
// Fetch from DB
// Create CartDAO from that object
// Return CartDAO
}
CartDAO fetchFromCassandra(String Id)
{
// Fetch from DB
// Create CartDAO from that object
// Return CartDAO
}
}
Now you can use CartDAO seamlessly in your application logic.
Yes it is possible.
Option 1:
Simply put the required annotation of both MongoDB and Cassandra.
Each annotation will have there own package and definition. So provide the required definition.
Option 2:
As defined by snk01, you can use that approach as well.
Here i am assuming that you are writing the persistence layer for each database seperately.

Retrieve Max value from a field using Spring Data and MongoDB

I want to obtain the maximum value of the field code within my User entity, using Spring Data and MongoDB.
I have seen similar examples using as below,
".find({}).sort({"updateTime" : -1}).limit(1)"
But have no idea how to integrate it into my own repository using the #Query annotation.
Any alternative solution, than to return the maximum value of said field is also welcome.
Thank you.
You can write a custom method for your repository.
For example you have:
public interface UserRepository extends MongoRepository<User, String>, UserRepositoryCustom {
...
}
Additional methods for repository:
public interface UserRepositoryCustom {
User maxUser();
}
And then implementation of it:
public class UserRepositoryImpl implements UserRepositoryCustom {
#Autowired
private MongoTemplate mongoTemplate;
#Override
public User maxUser() {
final Query query = new Query()
.limit(1)
.with(new Sort(Sort.Direction.DESC, "updateTime"));
return mongoTemplate.findOne(query, User.class)
}
}
You can use the spring data method syntax like:
public User findTopByOrderByUpdateTimeAsc()
A reference can be found here: https://www.baeldung.com/jpa-limit-query-results#1first-ortop
Use this code in spring to get the latest updated time from mongodb: (mongoTemplate)
public List getTopPosts() {
Query query = new Query();
query.with(Sort.by(Sort.Direction.DESC, "postUploadedTime"));
return mongoTemplate.find(query,Post.class);
}

Get spring-data-mongodb to honor getter/setter without backing field?

I have a general-purpose POJO:
public class Thing {
private String name;
private String etc;
public String getName() {
return name;
}
// other getters and setters
}
I'm using Spring 4.3.9 and Spring-data-mongodb 1.10.4. I want to store instances of this POJO in Mongodb, but I have some constraints:
I can't add Spring annotations to the base class (but I can subclass Thing and annotate that).
I want to use the name field as the Mongodb unique ID (mainly to avoid creating a separate unique index for it).
I want to (redundantly) store the name field as an actual field named "name", so that other consumers of the collection don't have to know that "name" is stored in the _id.
I started out trying this:
public class SpringThing extends Thing {
#Id
#Override
public String getName() {
return super.getName();
}
#Override
public void setName(String name) {
super.setName(name);
}
}
This causes spring to use the value of name for _id, but of course it doesn't store a field named "name" in Mongodb. The documentation says that spring will use a "property or field" named "id" or annotated with #Id. So I tried defining a redundant getter/setter which accesses the name field:
public class SpringThing extends Thing {
#Id
public String getId() {
return super.getName();
}
public void setId(String id) {
super.setName(id);
}
}
Unfortunately, spring ignores getId and setId here, and stores the object with an autogenerated ID. I also tried creating redundant getters/setters annotated with #Field("name"), but spring seems to ignore any getter/setter pair without an actual field.
Adding an actual ID field and storing a copy of the name there does work:
public class SpringThing extends Thing {
#Id
private String id;
#Override
public void setName(String id) {
this.id = id;
super.setName(id);
}
}
But it requires defining a pointless field named "id".
Is there a more reasonable way to get what I want? Is what I'm trying to do reasonable to begin with?
Thanks to a hint by #mp911de, I ended up creating a subclass of Thing that looks like this:
#TypeAlias("thing")
#Document(collection = "things")
public class SpringThing extends Thing {
#Id
#AccessType(Type.PROPERTY)
#JsonIgnore
public String getId() {
return super.getName();
}
public void setId(String taskName) {
super.setName(taskName);
}
}
The #TypeAlias annotation overrides the name which spring would use for the type, to cover up the fact that I've created a subclass just to add annotations.
#Id says that this is the getter for _id.
#AccessType says to access this field through the getter and setter rather than by directly accessing the field. This is what I needed; without it, spring looks for a private member variable named something like id.
#JsonIgnore is the Jackson (JSON library that we're using) annotation to prevent including the id field when serializing these objects to JSON.

How to use dynamic schema in spring data with mongodb?

Mongodb is a no-schema document database, but in spring data, it's necessary to define entity class and repository class, like following:
Entity class:
#Document(collection = "users")
public class User implements UserDetails {
#Id private String userId;
#NotNull #Indexed(unique = true) private String username;
#NotNull private String password;
#NotNull private String name;
#NotNull private String email;
}
Repository class:
public interface UserRepository extends MongoRepository<User, String> {
User findByUsername(String username);
}
Is there anyway to use map not class in spring data mongodb so that the server can accept any dynamic JSON data then store it in BSON without any pre-class define?
First, a few insightful links about schemaless data:
what does “schemaless” even mean anyway?
“schemaless” doesn't mean “schemafree”
Second... one may wonder if Spring, or Java, is the right solution for your problem - why not a more dynamic tool, such a Ruby, Python or the Mongoshell?
That being said, let's focus on the technical issue.
If your goal is only to store random data, you could basically just define your own controller and use the MongoDB Java Driver directly.
If you really insist on having no predefined schema for your domain object class, use this:
#Document(collection = "users")
public class User implements UserDetails {
#Id
private String id;
private Map<String, Object> schemalessData;
// getters/setters omitted
}
Basically it gives you a container in which you can put whatever you want, but watch out for serialization/deserialization issues (this may become tricky if you had ObjectIds and DBRefs in your nested document). Also, updating data may become nasty if your data hierarchy becomes too complex.
Still, at some point, you'll realize your data indeed has a schema that can be pinpointed and put into well-defined POJOs.
Update
A late update since people still happen to read this post in 2020: the Jackson annotations JsonAnyGetter and JsonAnySetter let you hide the root of the schemaless-data container so your unknown fields can be sent as top-level fields in your payload. They will still be stored nested in your MongoDB document, but will appear as top-level fields when the ressource is requested through Spring.
#Document(collection = "users")
public class User implements UserDetails {
#Id
private String id;
// add all other expected fields (getters/setters omitted)
private String foo;
private String bar;
// a container for all unexpected fields
private Map<String, Object> schemalessData;
#JsonAnySetter
public void add(String key, Object value) {
if (null == schemalessData) {
schemalessData = new HashMap<>();
}
schemalessData.put(key, value);
}
#JsonAnyGetter
public Map<String, Object> get() {
return schemalessData;
}
// getters/setters omitted
}

Ignore field from database Spring Roo

I have a spring roo web service that I am currently building out but I have an entity that contains a field that should not be included in the database.
I would like the field to be in the entity and print it out with JSON to string methods, but I don't need that value saved. Is there any annotation or hack to make this happen?
Spring Roo uses JPA for persistence. You want to mark the field as #Transient:
#RooJavaBean
#RooEntity
class MyEntity {
private String column1;
#Transient
private String ignoreMe; // Ignore this field in JPA
}
You can also use the same annotation for bean methods that would otherwise be mapped:
#RooJavaBean
#RooEntity
class MyEntity {
private String column1;
#Transient
private String getAsJSON() {
return JSONHelper.toJSON(column1);
}
}

Resources