I have a below problem and I am looking for solution in Spring - MVC and Hibernate only.
Problem Statement: There is UI which is having a text area and Submit button. In text area user can write a SQL query and after submitting, this SQL query has to be executed at multiple databases.
Note: Assume that multiple databases are already configured.
Please tell me the best approach for implementing it.
You might consider 2 options:
AbstractRoutingDataSource
With this solution you are able to switch to the right database before executing queries. So you could write something like
// GOLD database
CustomerContextHolder.setCustomerType(CustomerType.GOLD);
List<Item> goldItems = catalog.getItems();
assertEquals(3, goldItems.size());
System.out.println("gold items: " + goldItems);
// SILVER database
CustomerContextHolder.setCustomerType(CustomerType.SILVER);
List<Item> silverItems = catalog.getItems();
assertEquals(2, silverItems.size());
System.out.println("silver items: " + silverItems);
// DEFAULT database
CustomerContextHolder.clearCustomerType();
List<Item> bronzeItems = catalog.getItems();
assertEquals(1, bronzeItems.size());
System.out.println("bronze items: " + bronzeItems);
Here are some docs:
https://spring.io/blog/2007/01/23/dynamic-datasource-routing/
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSource.html
http://fedulov.website/2015/10/14/dynamic-datasource-routing-with-spring/
Using multiple transaction manager (one by database)
This involve that you know in advance what Transaction Manager you will need for a specific Service.
https://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/transaction.html#tx-multiple-tx-mgrs-with-attransactional
I will assume that you have multiple datasources wired in you application context (?). If so and assuming that they have different qualifiers, you can inject them into another component, typically a service which is invoked from your controller or directly into your controller.
#Autowired
List<DataSource> datasources;
Should do the trick, alternatively through constructor injection. With the latter, you can create a JdbcTemplate per DataSource. Then you loop through the list and execute the same sql query on all datasources.
Related
I have a spring-boot project and I am using Cassandra as database. My application is a tenant based application and all my tables include the tenantId. It is always part of the partition key of all tables but I have also other columns which are part of the partition keys.
So, the problem is; I want to remove a specific tenant from my database but I can't do it directly. Because I need the other part of the partition key.
I have two solutions for it in mind.
I will allow filtering and select all the tenant specific entities and then remove them one by one in the application.
I will use the findAll() method and fetch all the data and then filter in the application and delete all the tenant specific data.
Example:
public class DeleteTenant{
#Autowired MyRepository myRepo;
public void cleanTenantWithoutDbFiltering(String tenantId){
myRepo.findAll()
.stream()
.filter(entity -> entity.getTenantId().equals(tenantId)) // ??
.forEach(MyRepository::remove);
}
public void cleanTenantWithDbFiltering(String tenantId){
myRepo.getTenantSpecificData(tenantId)
.forEach(MyRepository::remove);
}
}
My getTenantSpecificData(String tenantId) query would look like that:
#AllowFiltering
#Query("Select * from myTable where tenantId = ?1 ALLOW FILTERING")
public List<MyEntity> getTenantSpecificData(String tenantId);
Do you have any other idea about it? If not which one do you think would be more efficient? Filtering in the application itself or in the cassandra.
Thanks in advance for your answers!
It isn't clear to me how you've modelled your data because you haven't provided examples of your schema but in any case, the use of ALLOW FILTERING is never going to be a good idea because it means that your query has to do a full table scan of all the relevant tables unless the tenant ID is the partition key.
You will need to come up with a different approach such as writing a Spark app that will efficiently go through the tables to identify partitions/rows to delete. Cheers!
how would you approach problem of simple app, allowing users to summarise/calculate average of price values of inventory stored? MVC model, Spring, H2. Do I need Hibernate to achieve that? How to access fields of particular items stored?
Is it a requirement to use H2? If no, just 'read' the inventory and calculate on the fly. Build the minimal solution.
If yes, I personally prefer to go with Spring Boot / Spring Data / Hibernate as this is widely used and better to maintain than a self-build solution. You could get the information with a custom query at the repository - something like:
#Query(value = "SELECT AVG(price) FROM product")
public Double getAveragePrice();
With https://bootify.io you can setup your Spring Boot app with the database model, and can add the custom logic on top.
To improve our query performances and hence the API response times, we created views on MongoDB by aggregating the data. However when we try to use the view using Spring Mongo template, running into several issues like View not supported.
Caused by: com.mongodb.MongoCommandException: Command failed with error 166 (CommandNotSupportedOnView): 'Namespace aiops.hostView is a view, not a collection' on server 192.168.20.166:30011. The full response is {"ok": 0.0, "errmsg": "Namespace aiops.hostView is a view, not a collection", "code": 166, "codeName": "CommandNotSupportedOnView"}
at com.mongodb.internal.connection.ProtocolHelper.getCommandFailureException(ProtocolHelper.java:175)
at com.mongodb.internal.connection.InternalStreamConnection.receiveCommandMessageResponse(InternalStreamConnection.java:303)
at com.mongodb.internal.connection.InternalStreamConnection.sendAndReceive(InternalStreamConnection.java:259)
Does Spring support MongoDB views out of the box? any example will greatly help!
Thank you in advance
This is somewhat old, but I will leave my solution in case someone stumbles on this, like I did.
As far as I know, you can't use the usual Spring Data approach: you can't annotate an Entity with the #Document(value="YOUR_VIEW_NAME") annotation and create a related repository extending the MongoRepository class.
But you can query the view directly from the MongoTemplate, passing along the name of your view.
Let's say you have a User entity defined like this:
public class User {
#Id
String id;
String name;
}
Then you can map it to the documents of a view named "userview", and query it like this:
Query query = new Query();
query.addCriteria(Criteria.where("name").is("Bob"));
// template is an object of class MongoTemplate that you can inject or autowire
List<User> users = template.find(query, User.class, "userview");
I recently faced the same problem, what #gere said is not entirely correct, you can use a view with spring data repositories but there are some limitations that are related to the limitation of the view itself.
In my case, the problem was that I was using annotation CompositeIndex and Indexed on the document which represents a view, and as MongoDB view uses the underlying collection indexes so it does not have the operation command to create an index for itself and when spring data tries to create an index on that view, MongoDB throws an exception. After removing those annotations, I was able to use them via my repository. I would suggest you use a read-only repository as well to prevent having a save method or delete as I think that is a view limitation too. if you search you can find examples of read-only repositories.
In your case, you need to find what operation you were trying to do on a normal collection that view does not support.
I know using Spring Data Rest I can use the in-built functionality of Pagination Like this
Page<Product> findByCategoryId(#RequestParam("id") Long id, Pageable pageable);
However, I am in project I am using Spring mvc #RestController and want to achive the same functionality
I tried like this:-
Session currentSession = entityManager.unwrap(Session.class);
Query<Product> theQuery = currentSession.createQuery("from Product", Product.class);
theQuery.setFirstResult((pageNumber-1) * pageSize); // This is 0 based
theQuery.setMaxResults(pageSize);
List<Product> dataList = theQuery.getResultList();
return dataList;
It works but I don't get the count of total number of records in the table.
And for UI pagination I need that.
So do I have to hit 2 queries everytime first like above then 1 query to fetch record size. (Which can cause data sync problems if records are updated)
Or
Is there a better way to achieve this in SINGLE QUERY
If you need the total number of records then you must create a second query.
You could do that in one query with a subquery but then you cannot use Entities as return type.
Regarding data sync problems: If you run both queries in the same transaction there is no problem.
Btw. why do you unwrap the Hiberante Session? There is no need for that in your example.
In a dao class implementation,I want to use different sql query depending upon the underlying database. Since my SQL query is complex which selects from a database view and uses "UNION" key word and uses database specific functions, I can not use JPQL (or HQL). I did some search on Stackoverflow and threads suggest the good way would be to find out the dialect used in the application. Can anyone provide some code example?
EDIT : My apologies, I did not explain my question well enough. In my dao class implementation , I want to determine the database ( say mysql or oracle) on which my application is running and then execute the appropriate query. I need the code like jdbcTemplate.findOutTheDialect().
JPA have the native queries for that.
An example you can find here.
You can use spring JdbcTemplate to connect and query from your database.
For Example..
String query = "SELECT COUNTRY_NAME FROM COUNTRY WHERE MCC_COUNTRY NOT IN ('Reserved') ORDER BY COUNTRY_NAME";
jdbcTemplate.query(query, new ObjectRowMapper());
Where "ObjectRowMapper" will be a class to map return resultset to list of Objects.