I try to establish a deleteAll function which deletes all documents which are associated with a given repository and class. In order to this I created a custom N1ql query. But I want the Couchbase index to be updated before later database operations take place. My guess is that I have to change the consistency level of the query to achieve this behaviour.
Here and here I found some examples which do this by using the CouchbaseTemplate. But my template is null. Could anybody tell me what I am doing wrong?
public void deleteAll() throws DBException {
CouchbaseOperations couchbaseTemplate;
try {
couchbaseTemplate = templateProvider.resolve(getRepository().getClass(), getClassName().getClass());
} catch (Exception e) {
throw new DBException("Could not get couchbase client", e);
}
String statement = String.format("DELETE FROM %s WHERE _class='%s'",
couchbaseTemplate.getCouchbaseBucket().name(), getClassName());
ScanConsistency consistency = couchbaseTemplate.getDefaultConsistency().n1qlConsistency();
N1qlParams queryParams = N1qlParams.build().consistency(consistency);
N1qlQuery query = N1qlQuery.simple(statement, queryParams);
N1qlQueryResult result = couchbaseTemplate.queryN1QL(query);
//Result handling
}
}
The templateProvider is autowired.
It is not entirely clear about your repository and entity from your code snippet. Which version of SDC are you using?
If you are using operation mapping bean, you get the underlying couchbase template for the particular repository and entity using
#Repository
public interface MyRepository extends CrudRepository<MyEntity, String> {
}
public class MyService {
#Autowired
MyRepository repo;
#Autowired
RepositoryOperationsMapping templateProvider;
....
CouchbaseOperations operations = templateProvider.resolve(repo.getClass(),MyEntity.class);
Make sure to enable couchbase repositories with #EnableCouchbaseRepositories. If your repositories only use couchbase, you can also get the couchbase template bean directly.
#Autowired
CouchbaseTemplate template;
Related
I am using spring data solr 3.0.6, and standalone solr server 7.0.0 and I have multiple solr cores in my solr server, and I want to choose one of them dynamically , Here is the configuration I Have
#Configuration
#EnableSolrRepositories(basePackages = "com.solr.repository")
public class SolrConfiguration
{
#Bean
public SolrOperations solrTemplate(SolrClient solr) {
return new SolrTemplate(solr);
}
#Bean(name = "solrClient")
public SolrClient createSolrClient()
{
HttpSolrClient.Builder builder = new HttpSolrClient.Builder().withBaseSolrUrl(solrInstanceUrl);
return builder.build();
}
}
SolrRepository
public interface SolrRepository extends SolrCrudRepository<SolrDocument, Integer>
{
#Query("name:*?0* OR content:*?0*")
#Highlight()
public HighlightPage<SolrDocument> findByQueryAnnotation(String searchTerm, Pageable pageable);
}
SolrDocument (domain class for Solr repository)
#SolrDocument
public class SolrDocument
{
}
ServicePojo (service class)
public class ServicePojo
{
#Autowired
SolrRepository solrRepository;
public void findData(int id)
{
solrRepository.findById(id);
}
}
Now I want to use methods of repository interface like findById() etc, but as I mentioned above, I have different cores, and I want to point a specific core to perform searching, before calling method solrRepository.findById() I need to mention which core it should point. So where we can tell to solr server , which Core to be used ?
If I use annotation #SolrDocument(collectionName="core1"), then it works fine and it points to "core1", but I want this to be dynamic. Please help
You cannot do this using Repository (SolrRepository). But you can do this using SolrTemplate.
Here is the link to the 3.0.6 SolrTemplate API. If you look at some of the methods like query or getById, it takes an argument collectionName. You can pass coreName here.
I have a Spring Boot project which has multiple Model class
I want to use multiple model classes in my DAO implementation.
Can someone please explain how can I use HQL queries here.
Most of the examples i saw uses CRUDRepository. But in my understanding this limits us to one model class (please correct me if my understanding is wrong).
Secondly using CRUDRepository limits me to very specific methods. But HQL queries I need could be more complex like:
Query query = session.createQuery(from Employee where name=:name and place=:place and phone=:phone and gender=:gender);
Is it standard practice to use CRUDRepository even for HQL queries like this.
Otherwise how can I use such HQL queries. (If possible please also add what all dependencies and annotations i will need to add). I am using Hibernate and postgres.
If you wish to execute dynamic HQL queries without using repositories, etc, this works for me really nicely:
#Autowired
EntityManager entityManager;
#RequestMapping("/query")
#ResponseBody
public String testQuery() {
Query query = entityManager.createQuery("select u from User u");
List<User> users = query.getResultList();
users.forEach(u -> System.out.println(u.getFirstname()));
return "See Console";
}
The nice thing about it is that if you're quickly wanting to test a query, and you're using something like Spring LiveReload, JRebel, DCEVM or HotSwap with Hybris, you can change the query and save and refresh.
CRUD repositories are the standard way to write a DAO. If you look at the documentations, you will see that you can have complex queries that uses the fields of the entity (which can be pretty complex, I encourage you to look here at the query methods part - 4.3), and if you want even more complex methods, you can use the annotation #Query over a method:
#Repository
interface MyRepo implements JpaRepository<MyEntity,Long> {
#Query("your custom query here")
MyEntity findByFeature(String a)
}
Now you can insert you query and access you String a inside the query (like Select u where u.name="blabla" where blabla is your input string)
The #Query comes with the same dependency as the repositories (I think that spring-boot-starter-data-jpa is enough)
JpaRepository is surely awesome to use, but for someone who has to use complex HQL queries (due to your organization's recommendations etc), here is how i did it:
Get Session factory by injecting following bean in your configuration:
#Bean
public SessionFactory sessionFactory(#Qualifier("entityManagerFactory") EntityManagerFactory emf)
{
return emf.unwrap(SessionFactory.class);
}
Add EntityScan and ComponentScan to your configuration class
#EntityScan(basePackages = { "com.app.persistence" })
#ComponentScan(basePackages = { "com.app" })
Go ahead and use any HQL queries:
#Autowired
SessionFactory factory;
#Override
public String check() {
Session session = null;
try {
session = factory.openSession();
Query query = session.createQuery("from Test");
List<Test> res = query.list();
Test sing = res.get(0);
return sing.getName();
} catch (Exception e) {
System.out.println("Exception in Dao");
e.printStackTrace();
throw e;
} finally {
if (session != null) {
session.close();
}
}
}
Note: I am assuming that other things like configuring DataSource and all have been taken care already.
Anyone can feel free to correct me if my understanding or way was wrong.
But it worked well for me this way!! :)
I would like to use Oracle NoSQL database together with Spring data. The aim is to access the data over spring data repositories and even use spring data rest on top of it.
So I think the spring-data-keyvalue project would help me, to implement an adapter for Oracle NoSQL KV.
I tried to understand the documentation of spring-data-keyvalue (http://docs.spring.io/spring-data/keyvalue/docs/current/reference/html/#key-value.core-concepts), but didn't get the idea.
An example/tutorial about how to implement an adapter from scratch would be very helpful.
What I have is this configuration class where I provide a custom KeyValueAdapter. Now if I use CrudRepository methods it uses my custom adapter.
#Configuration
#EnableMapRepositories
public class KeyValueConfig {
#Bean
public KeyValueOperations keyValueTemplate() {
return new KeyValueTemplate(new OracleKeyValueAdapter());
}
}
The OracleKeyValueAdapter is an implementation of KeyValueAdapter. I got this from the spring-data-keyvalue-redis project (https://github.com/christophstrobl/spring-data-keyvalue-redis/blob/master/src/main/java/org/springframework/data/keyvalue/redis/RedisKeyValueAdapter.java)
public class OracleKeyValueAdapter extends AbstractKeyValueAdapter {
private KVStore store;
public OracleKeyValueAdapter() {
String storeName = "kvstore";
String hostName = "localhost";
String hostPort = "5000";
store = KVStoreFactory.getStore
(new KVStoreConfig(storeName, hostName + ":" + hostPort));
}
//Custom implementations:
#Override
public Object put(Serializable serializable, Object o, Serializable
serializable1) {
return null;
}
#Override
public boolean contains(Serializable serializable, Serializable
serializable1) {
return false;
}
.
.
.
Now I'm trying to implement this OracleKeyValueAdapter, but i don't know if that does even make sense.
Can you help me?
You might want to start with how spring-data-keyvalue is implemented over Redis, the link here should be a good starting point - http://docs.spring.io/spring-data/data-keyvalue/docs/1.0.0.BUILD-SNAPSHOT/reference/redis.html
Let me know how that goes, I am interested in what you are trying to accomplish.
The following configuration should work (tested on v2.4.3)
#Configuration
#EnableMapRepositories
public class Configuration {
#Bean
public KeyValueOperations mapKeyValueTemplate() {
return new KeyValueTemplate(keyValueAdapter());
}
#Bean
public KeyValueAdapter keyValueAdapter() {
return new YourKeyValueAdapter();
}
}
The name (mapKeyValueTemplate) of the KeyValueOperations bean is important here but it can also be changed as followed:
#Configuration
#EnableMapRepositories(keyValueTemplateRef = "foo")
public class Configuration {
#Bean
public KeyValueOperations foo() {
return new KeyValueTemplate(keyValueAdapter());
}
#Bean
public KeyValueAdapter keyValueAdapter() {
return new YourKeyValueAdapter();
}
}
I saw sources of Spring KeyValue Repository:
https://github.com/spring-projects/spring-data-keyvalue
I recomend to understand, how Spring Repository work inside.
If you want to realise own repository (CustomKeyValueRepository), you must create at least 6 classes:
EnableCustomKeyValueRepositories - annotation to enable repository type in your project.
CustomKeyValueRepositoriesRegistrar - registrator for this annotaion.
CustomKeyValueRepository - repository
CustomKeyValueRepositoryConfigurationExtension - implementation of Spring ConfigurationExtension.
CustomKeyValueAdapter - implementation of custom adapter for your data store.
CustomKeyValueConfiguration - configuration of beans Adapter and Template.
I code Infinispan KeyValue Repository by this way:
https://github.com/OsokinAlexander/infinispan-spring-repository
I also write article about this:
https://habr.com/ru/post/535218/
In Chrome you can translate it to your language.
The simplest way you can try implement only CustomKeyValueAdapter and Configuration. In Configuration you must redefine Spring KeyValueAdapter bean and KeyValueTemplate (it is very important that the name of the bean is with a lowercase letter, that's the only way it worked for me):
#Configuration
public class CustomKeyValueConfiguration extends CachingConfigurerSupport {
#Autowired
private ApplicationContext applicationContext;
#Bean
public CustomKeyValueAdapter getKeyValueAdapter() {
return new CustomKeyValueAdapter();
}
#Bean("keyValueTemplate")
public KeyValueTemplate getKeyValueTemplate() {
return new KeyValueTemplate(getKeyValueAdapter());
}
}
We have implemented an application that should be able to use either JPA, Couchbase or MongoDB. (for now, may increase in the future). We successfully implemented JPA and Couchbase by separating repositories for each e.g. JPA will come from org.company.repository.jpa while couchbase will come from org.company.repository.cb. All repository interfaces extends a common repository found in org.company.repository. We are now targeting MongoDB by creating a new package org.company.repository.mongo. However we are encountering this error:
No property updateLastUsedDate found for type TokenHistory!
Here are our codes:
#Document
public class TokenHistory extends BaseEntity {
private String subject;
private Date lastUpdate;
// Getters and setters here...
}
Under org.company.repository.TokenHistoryRepository.java
#NoRepositoryBean
public interface TokenHistoryRepository<ID extends Serializable> extends TokenHistoryRepositoryCustom, BaseEntityRepository<TokenHistory, ID> {
// No problem here. Handled by Spring Data
TokenHistory findBySubject(#Param("subject") String subject);
}
// The custom method
interface TokenHistoryRepositoryCustom {
void updateLastUsedDate(#Param("subject") String subject);
}
Under org.company.repository.mongo.TokenHistoryMongoRepository.java
#RepositoryRestResource(path = "/token-history")
public interface TokenHistoryMongoRepository extends TokenHistoryRepository<String> {
TokenHistory findBySubject(#Param("subject") String subject);
}
class TokenHistoryMongoRepositoryCustomImpl {
public void updateLastUsedDate(String subject) {
//TODO implement this
}
}
And for Mongo Configuration
#Configuration
#Profile("mongo")
#EnableMongoRepositories(basePackages = {
"org.company.repository.mongo"
}, repositoryImplementationPostfix = "CustomImpl",
repositoryBaseClass = BaseEntityRepositoryMongoImpl.class
)
public class MongoConfig {
}
Setup is the same for both JPA and Couchbase but we didn't encountered that error. It was able to use the inner class with "CustomImpl" prefix, which should be the case base on the documentations.
Is there a problem in my setup or configuration for MongoDB?
Your TokenHistoryMongoRepositoryCustomImpl doesn't actually implement the TokenHistoryRepositoryCustom interface, which means that there's no way for us to find out that updateLastUsedDate(…) in the class found is considered to be an implementation of the interface method. Hence, it's considered a query method and then triggers the query derivation.
I highly doubt that this works for the other stores as claimed as the code inspecting query methods is shared in DefaultRepositoryInformation.
It appears that the update for mongoOperations do not trigger the events in AbstractMongoEventListener.
This post indicates that was at least the case in Nov 2014
Is there currently any way to listen to update events like below? This seems to be quite a big omission if it is the case.
MongoTemplate.updateMulti()
Thanks!
This is no oversight. Events are designed around the lifecycle of a domain object or a document at least, which means they usually contain an instance of the domain object you're interested in.
Updates on the other hand are completely handled in the database. So there are no documents or even domain objects handled in MongoTemplate. Consider this basically the same way JPA #EntityListeners are only triggered for entities that are loaded into the persistence context in the first place, but not triggered when a query is executed as the execution of the query is happening in the database.
I know it's too late to answer this Question, I have the same situation with MongoTemplate.findAndModify method and the reason I needed events is for Auditing purpose. here is what i did.
1.EventPublisher (which is ofc MongoTemplate's methods)
public class CustomMongoTemplate extends MongoTemplate {
private ApplicationEventPublisher applicationEventPublisher;
#Autowired
public void setApplicationEventPublisher(ApplicationEventPublisher
applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
//Default Constructor here
#Override
public <T> T findAndModify(Query query, Update update, Class<T> entityClass) {
T result = super.findAndModify(query, update, entityClass);
//Publishing Custom Event on findAndModify
if(result!=null && result instanceof Parent)//All of my Domain class extends Parent
this.applicationEventPublisher.publishEvent(new AfterFindAndModify
(this,((Parent)result).getId(),
result.getClass().toString())
);
return result;
} }
2.Application Event
public class AfterFindAndModify extends ApplicationEvent {
private DocumentAuditLog documentAuditLog;
public AfterFindAndModify(Object source, String documentId,
String documentObject) {
super(source);
this.documentAuditLog = new DocumentAuditLog(documentId,
documentObject,new Date(),"UPDATE");
}
public DocumentAuditLog getDocumentAuditLog() {
return documentAuditLog;
}
}
3.Application Listener
public class FindandUpdateMongoEventListner implements ApplicationListener<AfterFindAndModify> {
#Autowired
MongoOperations mongoOperations;
#Override
public void onApplicationEvent(AfterFindAndModify event) {
mongoOperations.save(event.getDocumentAuditLog());
}
}
and then
#Configuration
#EnableMongoRepositories(basePackages = "my.pkg")
#ComponentScan(basePackages = {"my.pkg"})
public class MongoConfig extends AbstractMongoConfiguration {
//.....
#Bean
public FindandUpdateMongoEventListner findandUpdateMongoEventListner(){
return new FindandUpdateMongoEventListner();
}
}
You can listen to database changes, even the changes completely outside your program (MongoDB 4.2 and newer).
(code is in kotlin language. same for java)
#Autowired private lateinit var op: MongoTemplate
#PostConstruct
fun listenOnExternalChanges() {
Thread {
op.getCollection("Item").watch().onEach {
if(it.updateDescription.updatedFields.containsKey("name")) {
println("name changed on a document: ${it.updateDescription.updatedFields["name"]}")
}
}
}.start()
}
This code only works when replication is enabled. You can enable it even when you have a single node:
Add the following replica set details to mongodb.conf (/etc/mongodb.conf or /usr/local/etc/mongod.conf or C:\Program Files\MongoDB\Server\4.0\bin\mongod.cfg) file
replication:
replSetName: "local"
Restart mongo service, Then open mongo console and run this command:
rs.initiate()