Spring Data MongoDB document expiry time - spring

I've set up a simple document model (below) along with a Spring Data repository
#Document(collection = "users")
public class UserDocument {
#Id
private String userId;
#Indexed(expireAfterSeconds=3600)
private LocalDateTime registeredDate;
}
This seems to work fine and deletes the user documents after whatever time I set expireAfterSeconds to. However, rather than typing in a value of 3600 I'd like to pull that number from a config .yml. The usual way of adding #Value("${config.file.path.of.expiry}") won't work because #Indexedrequires the value to be a runtime constant, which #Value isn't.
Is there any other way to set up document expiry dates in Spring/Mongodb that doesn't use #Indexed(expireAfterSeconds=x)?

Instead of using the Indexed annotation to declare an index which Spring Data Mongo will create on your behalf ... you could create the index explicitly using a MongoTemplate instance.
#Value("${config.file.path.of.expiry}")
long expireAfterSeconds;
...
mongoTemplate.indexOps("users").ensureIndex(
new Index().on("registeredDate", Order.ASCENDING)
.expire(expireAfterSeconds)
);
This would allow you to source the value for expireAfterSeconds from a configuration file at runtime rather than hardcoding it in an annotation.

Related

How to make #Indexed as unique property for Redis model using Spring JPA Repository?

I have a model class that I store in Redis and I use Jpa Repository with Spring java. Normally(not with redis) jpa repository is saving the new data or updates(conditionally) if the given model is already exist in Db. Here, I want to add new item to redis but if it is not already exists on db otherwise update it just like usual Jpa implementation.
Here is my model:
#Getter
#Setter
#RedisHash("MyRecord")
public class MyRecordRedisModel {
private String id;
#Id
#Indexed
private String recordName;
private Date startDate;
private Date endDate;
}
And my repository class is just a normal spring jpa repo as follows:
#Repository
public interface IFRecordRedisRepository extends JpaRepository<IFRecordRedisModel, String> {
Page<IFRecordRedisModel> findAll(Pageable pageable);
}
Unique key must be the name (I totally do not care about uniquiness of the id). Thus, if the name is already exist in Db than do not add it again. I marked it as Indexed but still it is adding same data (with same recordName).
How can I make it unique?
This would require an additional query, but I think this solution would work for you. You can use query by Example to check if there exists a record with that name, and save conditionally, or do something else if it already exists.
IFRecordRedisModel exampleRecord = new IFRecordRedisModel();
exampleRecord.setRecordName(inputRecord.getRecordName());
if (!repository.exists(Example.of(exampleModel)))
repository.save(inputRecord);
else ..... // do something else

how to insert a object in mongodb using spring

required format image
I want to object data into MongoDB using spring and I have hardcoded it.
please how to write a schema for that and I have taken it as an example only.
I have a different type of categories in it I have taken only clothes.
please tell me how to write one schema for a different type of categories and query too.
please find the attachment for your reference
I would recommend going though Spring Data MongoDB documentation for specifics on mapping java objects to MongoDB documents. Your case would look similar to:
#Document
public class Clothes {
#Id
private ObjectId id;
private Men men;
private Women women;
// getters & setters
}
You would need to define each sub class but this should be the gist of it.
What you can do is create a simple POJO (Plain Old Java Object) and with that you can insert that object into the data base. The the following example:
#Document
public class OAuthModel implements Serializable {
#Id
String username;
#Indexed
String oAuthID;
#Indexed
String type;
// Getter and Setters and Construct.
}
When I insert this object in the DB by calling:
OAuthModel authModel = new OAuthModel(username,firebaseToken.getUid(), OAuthHosts.GOOGLE.getType());
oAuthRepo.insert(authModel);
It will then be seen as this in the Database:
Keep in mind this will work no matter what your object looks like, you can have hashmaps etc. The should be a built in serialization.

JPA - Auto-generated field null after save

I have an Account entity and I'm trying to persist it using save function. My code:
#Override
public Account createAccount(String pin) {
Account account = new Account();
account.setBalance(0L);
account.setPin(pin);
return accountRepository.save(account);
}
Now my entity class has an autogenerated field called accountNumber. My entity class:
#Entity
#Table(name = "accounts")
#Data
public class Account {
#Column(name = "account_number", length = 32, insertable = false)
private String accountNumber;
private Long balance;
}
Now after calling save, the entity returned has accountNumber as null but i can see in the intellij database view that it is actually not null. All the other auto-generated fields like id etc are there in the returned entity just the accountNumber is null. Default value for accountNumber is set in the sql file :
ALTER TABLE accounts
ALTER COLUMN account_number SET DEFAULT DefaultValueSerializer(TRUE, TRUE, 12);
Here, DefaultValueSerializer is the function which is generating the account number.
I've tried other solutions available here like using saveAndFlush() etc, nothing worked in my case. What can be an issue?
As mentioned in comment Hibernate is not aware about what happens in database engine level so it does not see the value generated.
It would be wise to move generation of account number to JPA level instead of using db defaults.
I suggest you to study annotations #GeneratedValue and related stuff like #SequenceGenerator. That way the control of generating account number is in JPA level and there is no need for stuff like refreshing entity after save.
One starting point: Java - JPA - Generators - #SequenceGenerator
For non-id fields it is possible to generate value in method annotated with #PrePersist as other answer suggests but you could do the initialization already in the Accounts constructor .
Also see this answer for options.
You can create an annotated #PrePersist method inside the entity in which you set its fields to their default value.
That way jpa is going to be aware of the default.
There are other such annotation avaiable for different entity lifecycle event https://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/listeners.html
P.s. if you decide to go this way remember to remove the insertable = false
Use
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
for your IDs. And also leave your saving to saveAndFlush so you can immediately see the changes, If any. I'd also recommend separating IDs and account numbers. They should not be the same. Try debugging your program and see where the value stops passing around.

Spring Boot - Change connection dynamically

I have a Spring Boot project with multiple databases of different years and these databases have same tables so the only difference is the year (..., DB2016, DB2017). In the controller of the application i need to return data that belong to "different" years. Moreover in future years other databases will be created (eg. in 2018 there's going to be a db named "DB2018"). So my problem is how to switch the connection among databases without creating a new datasource and a new repository every new year.
In an other question posted by me (Spring Boot - Same repository and same entity for different databases) the answer was to create different datasources and different repositories for every existing database, but in this case i want to return data from existing databases on the basis of the current year. More specifically:
SomeEntity.java
#Entity(name = "SOMETABLE")
public class SomeEntity implements Serializable {
#Id
#Column(name="ID", nullable=false)
private Integer id;
#Column(name="NAME")
private String name;
}
SomeRepository.java
public interface SomeRepository extends PagingAndSortingRepository<SomeEntity, Integer> {
#Query(nativeQuery= true, value = "SELECT * FROM SOMETABLE WHERE NAME = ?1")
List<SomeEntity> findByName(String name);
}
SomeController.java
#RequestMapping(value="/foo/{name}", method=RequestMethod.GET)
public ResponseEntity<List<SomeEntity>> findByName(#PathVariable("name") String name) {
List<SomeEntity> list = autowiredRepo.findByName(name);
return new ResponseEntity<List<SomeEntity>>(list,HttpStatus.OK);
}
application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/DB
spring.datasource.username=xxx
spring.datasource.password=xxx
So if the current year is 2017 i want something like this:
int currentyear = Calendar.getInstance().get(Calendar.YEAR);
int oldestDbYear = 2014;
List<SomeEntity> listToReturn = new LinkedList<SomeEntity>();
//the method getProperties is a custom method to get properties from a file
String url = getProperties("application.properties", "spring.datasource.url");
props.setProperty("user", getProperties("application.properties","spring.datasource.username"));
props.setProperty("password", getProperties("application.properties","spring.datasource.password"));
for (int i = currentYear, i>oldestDbYear, i--) {
//this is the connection that must be used by autowiredRepo Repository, but i don't know how to do this.
//So the repository uses different connection for every year.
Connection conn = getConnection(url+year,props);
List<SomeEntity> list_of_specific_year = autowiredRepo.findByName(name);
conn.close;
listToReturn.addAll(list_of_specific_year);
}
return listToReturn;
Hope everithing is clear
The thing that is probably most suitable to your needs here is Spring's AbstractRoutingDataSource. You do need to define multiple DataSources but you will only need a single repository. Multiple data sources is not an issue here as there is always a way to create the DataSource beans programatically at run time and register them with the application context.
How it works is you basically register a Map<Object, DataSource> inside your #Configuration class when creating your AbstractRoutingDataSource #Bean and in this case the lookup key would be the year.
Then you need create a class that implements AbstractRoutingDataSource and implement the determineCurrentLookupKey() method. Anytime a database call is made, this method is called in the current context to lookup which DataSource should be returned. In your case it sounds like you simply want to have the year as a #PathVariable in the URL and then as the implementation of determineCurrentLookupKey() grab that #PathVariable out of the URL (e.g in your controller you have mappings like #GetMapping("/{year}/foo/bar/baz")).
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder
.getRequestAttributes()).getRequest();
HashMap templateVariables =
(HashMap)request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
return templateVariables.get("year");
I used this approach when writing a testing tool for a product where there were many instances running on multiple different servers and I wanted a unified programming model from my #Controllers but still wanted it to be hitting the right database for the server/deployment combination in the url. Worked like a charm.
The drawback if you are using Hibernate is that all connections will go through a single SessionFactory which will mean you can't take advantage of Hibernate's 2nd level caching as I understand it, but I guess that depends on your needs.

Get last created document in Mongodb using Spring Data repository

I'm trying to get the created datetime of the last created item in a mongodb repository.
I could obviously use a findAll(Sort sort) function, and get the first element, but this would not be very practical on a large database.
Mongo queries do not support an "orderBy" query method so this is also not a solution.
The order of creation is in chronological order of "created" so if I can get the last created document in the collection that would be good too.
So my question is:
What is the best way to retrieve the last created document in a mongodb repo using Spring data?
My current code:
#Data
#Document
public class Batch
{
#Id
String id;
LocalDateTime created;
//other stuff
}
public interface BatchRepository extends MongoRepository<Batch,String>
{
//this does not work
//Batch findOneOrderByCreatedDesc();
}
Try the following one, it should work well
public interface BatchRepository extends MongoRepository<Batch,String>
{
Batch findTopByOrderByCreatedDesc();
}
Notice that the method name slightly differs from your variant, this difference is important as spring parses the method name and builds a query based on the result of parsing.

Resources