how to migrate search query from ElasticsearchRepository to ElasticsearchOperations - spring-data-elasticsearch

My repository class uses the queries by using the standard method name findByxxx. It also has custom query using NativeSearchQueryBuilder.
Since search() methods have been removed from ElasticsearchRepository, I will create corresponding class to use ElasticsearchOperations. My code is like below.
#Repository
public interface BookRepository extends ElasticsearchRepository<Book, String> {
public Optional<Book> findById(String bookId);
//default public Page<Book> fetchBooksForUser(String userId, Pageable pageable) {...}
}
#Service
public class BookOperation{ //new class
#Autowired
private ElasticsearchOperations elasticsearchOperations;
public Page<Book> fetchBooksForUser(String userId, Pageable pageable) {..use elasticsearchOperations...}
}
#Service
public class BookServiceImpl implements BookService {
#Autowired
private BookRepository bookRepository;
#Autowired
private BookOperation bookOperation;
//some methods use bookRepository, some methods use formsPackageOperation
}
I have more than 20 Repository classes. I don't want to repeat this 20 times. Is there a better to do this?

You just need to create a repository fragment, check tyhe documentation at https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#repositories.custom-implementations.
In the interface you define the method you need and in the Impl file you implement the necessary logic - you can just inject an ElasticsearchOperations into the implementations class.
As for the changes in your existing repositories: You just need to add your custom interface to the implements clause.

Related

Getting an injected object using CDI Produces

I have a class (OmeletteMaker) that contains an injected field (Vegetable). I would like to write a producer that instantiates an injected object of this class. If I use 'new', the result will not use injection. If I try to use a WeldContainer, I get an exception, since OmeletteMaker is #Alternative. Is there a third way to achieve this?
Here is my code:
#Alternative
public class OmeletteMaker implements EggMaker {
#Inject
Vegetable vegetable;
#Override
public String toString() {
return "Omelette: " + vegetable;
}
}
a vegetable for injection:
public class Tomato implements Vegetable {
#Override
public String toString() {
return "Tomato";
}
}
main file
public class CafeteriaMainApp {
public static WeldContainer container = new Weld().initialize();
public static void main(String[] args) {
Restaurant restaurant = (Restaurant) container.instance().select(Restaurant.class).get();
System.out.println(restaurant);
}
#Produces
public EggMaker eggMakerGenerator() {
return new OmeletteMaker();
}
}
The result I get is "Restaurant: Omelette: null", While I'd like to get "Restaurant: Omelette: Tomato"
If you provide OmeletteMaker yourself, its fields will not be injected by the CDI container. To use #Alternative, don't forget specifying it in the beans.xml and let the container instantiate the EggMaker instance:
<alternatives>
<class>your.package.path.OmeletteMaker</class>
</alternatives>
If you only want to implement this with Producer method then my answer may be inappropriate. I don't think it is possible (with standard CDI). The docs says: Producer methods provide a way to inject objects that are not beans, objects whose values may vary at runtime, and objects that require custom initialization.
Thanks Kukeltje for pointing to the other CDI question in comment:
With CDI extensions like Deltaspike, it is possible to inject the fields into an object created with new, simply with BeanProvider#injectFileds. I tested this myself:
#Produces
public EggMaker eggMakerProducer() {
EggMaker eggMaker = new OmeletteMaker();
BeanProvider.injectFields(eggMaker);
return eggMaker;
}

How to inject a list with different implementations of the same interface in a nested module scenario via Guice?

There is an interface DCE, which is implemented by a class DCEImpl which has a dependency, say, string S, which it gets via its constructor.
The universe of S is limited, say S can only take values {'A','B','C'}.
There is an already existing Guice module that accepts the value of S in its constructor, and then binds the interface DCE to the correctly initialized version of DCEImpl.
public class DCEModule extends AbstractModule {
private final String s;
public DCEModule(String s){
this.s = s;
}
protected void configure() {
bind(DCE.class).toInstance(new DCEImpl(s));
}
}
Now I have a class C which needs a List<DCE> with all the 3 implementations (actually a lot more than 3, using 3 for example purpose).
I want to inject this list via Guice in C. To do that, I created a new module DCEPModule, which will provide a List<DCE> in this way:
#Provides
List<DCE> getDCE() {
for(String s: S){
Module m = new DCEModule(s);
install(m);
Injector injector = Guice.createInjector(m);
listDomains.add(injector.getInstance(DCE.class));
}
}
My problem is that I don't want to call a new injector in this module, because DCEPModule will be installed by a different module.
public class NewModule extends AbstractModule {
protected void configure() {
install(DCEPModule);
}
}
I want a way to get the List<DCE> without explicitly creating a new injector in DCEPModule.
You can achieve this by using a Multibinder (javadoc, wiki).
Here’s an example:
public class SnacksModule extends AbstractModule {
protected void configure(){
Multibinder<Snack> multibinder = Multibinder.newSetBinder(binder(), Snack.class);
multibinder.addBinding().toInstance(new Twix());
  multibinder.addBinding().toProvider(SnickersProvider.class);
  multibinder.addBinding().to(Skittles.class);
}
}
Now, the multibinder will provide a Set<Snack>. If you absolutely need a List instead of a Set, then you can add a method to your module like this:
#Provides
public List<Snack> getSnackList(Set<Snack> snackSet) {
return new ArrayList(snackSet);
}
You can add implementations to the same Multibinding in more than one module. When you call Multibinder.newSetBinder(binder, type) it doesn’t necessarily create a new Multibinding. If a Multibinding already exists for for that type, then you will get the existing Multibinding.

How to inject a dependency into a repository base class

The various #EnableXXXRepository annotations of Spring Data allow you to specify a custom base class for your repositories, which will be used as an implementation of methods in your repository.
If such a base class needs access to other beans in the ApplicationContext how does one get those injected? It doesn't work out of the box, because Spring Data instantiates those base classes itself, supporting only special store dependent constructor parameters.
Note: I created this answer in the chat to this now deleted question and thought it might be valuable for others, although the original question is gone.
In the #Enable...Repository annotation specify a repositoryBaseClass and repositoryFactoryBeanClass. Like this:
#EnableMongoRepositories(
repositoryBaseClass = MyBaseClass.class,
repositoryFactoryBeanClass = MyRepositoryFactoryBean.class)
In that RepositoryFactoryBean class, you can use normal dependency injection, because it is a Spring Bean, so, for example, you can get an instance of SomeBean injected via the constructor, as shown below:
public class MyRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends MongoRepositoryFactoryBean<T,S,ID>{
private final SomeBean bean;
public MyRepositoryFactoryBean(Class repositoryInterface, SomeBean bean) {
super(repositoryInterface);
this.bean = bean;
}
}
Your RepositoryFactoryBean now create an instance of a custom RepositoryFactory by overwriting 'getFactoryInstance'.
#Override
protected RepositoryFactorySupport getFactoryInstance(MongoOperations operations) {
return new MyMongoRepositoryFactory(operations, bean);
}
While doing so, it can pass on the bean to be injected. bean in the example above.
And this factory finally instantiates your repository base class. Probably the best way to do it is to delegate everything to the existing factory class and just add injecting of the dependency to the mix:
public class MyMongoRepositoryFactory extends MongoRepositoryFactory {
private final SomeBean bean;
MyMongoRepositoryFactory(MongoOperations mongoOperations, SomeBean bean) {
super(mongoOperations);
this.bean = bean;
}
#Override
protected Object getTargetRepository(RepositoryInformation information) {
Object targetRepository = super.getTargetRepository(information);
if (targetRepository instanceof MyBaseClass) {
((MyBaseClass) targetRepository).setSomeBean(bean);
}
return targetRepository;
}
}
There is a complete working example on Github.

Spring security example with jdbc api

I downloaded appfuse struts2+spring secutiry which is using the hibernate.
I want to know how to convert it into jdbc api or provide other sample example which is developed using simple jdbc api.
Thanks
I think this can be achieved quite easily.All you need is to write implementation of "GenericDao" interface. Appfuse provides GenericDao hibernate implementation called "GenericDaoHibernate" out of the box, which I encourage you to use instead. Anyway, this is what I suggest:
Create a package called ...dao.jdbc
Create a JDBC implementation class for GenericDao Interface called
"GenericDaoJdbc" in above package. It may initially looks like below in code section.
Then you can continue implementing the rest of the interface methods with jdbcTemplate instance by getJdbcTemplate()
public class GenericDaoJdbcTemplate<T, PK extends Serializable>
implements GenericDao<T, PK> {
#Autowired
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
protected final Log log = LogFactory.getLog(getClass());
private Class<T> persistentClass;
public GenericDaoJdbcTemplate(final Class<T> persistentClass) {
this.persistentClass = persistentClass;
}
protected JdbcTemplate getJdbcTemplate(){
if (jdbcTemplate == null) return new JdbcTemplate(dataSource);
return jdbcTemplate;
}
#Override
public List<T> getAll() {
// TODO Auto-generated method stub
return null;
}
...
}

How to pass controller's ModelState to my service constructor with Autofac?

I have a wrapper on ModelStateDictionary which all my services accept. Is it possible to configure the autofac to inject the controller ModelStateDictionary into the constructor of the wrapper and then inject it into service constructor?
//code
public class ModelValidation : IModelValidation {
public ModelValidation(ModelStateDictionary msd){...}
..
..
}
public class CustomerService{
public CustomerService(IModelValidation mv){...}
..
}
Thanks
Based on your comments I hereby revise my answer :)
ModelStateDictionary is clearly not a service that should be resolved by the container, but rather data that should be provided at instantiation time. We can tell that from the fact that ModelState is owned by each Controller instance and is thus not available to the container at "resolve time".
Furthermore, each ModelValidation instance will be bound to a ModelStateDictionary instance and is thus also to be considered as data.
In Autofac, when data must be passed to constructors (optionally in addition to other dependencies), we must use factory delegates. These delegates will handle dependency and data passing to the constructor. The sweet thing with Autofac is that these delegates can be autogenerated.
I propose the following solution:
Since both ModelValidation and CustomerService requires data in their constructors, we need two factory delegates (note: the parameter names must match the names in their corresponding constructor):
public delegate IModelValidation ModelValidationFactory(ModelStateDictionary msd);
public delegate CustomerService CustomerServiceFactory(ModelStateDictionary msd);
Since your controllers shouldn't know where these delegates comes from they should be passed to the controller constructor as dependencies:
public class EditCustomerController : Controller
{
private readonly CustomerService _customerService;
public EditCustomerController(CustomerServiceFactory customerServiceFactory
/*, ...any other dependencies required by the controller */
)
{
_customerService = customerServiceFactory(this.ModelState);
}
}
The CustomerService should have a constructor similar to this (optionally handle some of this in a ServiceBase class):
public class CustomerService
{
private readonly IModelValidation _modelValidation;
public CustomerService(ModelStateDictionary msd,
ModelValidationFactory modelValidationFactory)
{
_modelValidation = modelValidationFactory(msd);
}
To make this happen we need to build our container like this:
var builder = new ContainerBuilder();
builder.Register<ModelValidation>().As<IModelValidation>().FactoryScoped();
builder.Register<CustomerService>().FactoryScoped();
builder.RegisterGeneratedFactory<ModelValidationFactory>();
builder.RegisterGeneratedFactory<CustomerServiceFactory>();
builder.Register<EditCustomerController>().FactoryScoped();
So, when the controller is resolved (eg when using the MvcIntegration module), the factory delegates will be injected into the controllers and services.
Update: to cut down on required code even more, you could replace CustomerServiceFactory with a generic factory delegate like I've described here.
Builder.RegisterInstance(new ModelStateDictionary()).SingleInstance();
builder.Register(c => new SW.PL.Util.ModelStateWrapper
(c.Resolve<ModelStateDictionary>())).As<IValidationDictionary>().InstancePerHttpRequest();
Add a new constructor without ValidationService. Assign the ValidationService by using a property.
The property must be implemented in the interface ICostumerService
public class ModelStateWrapper: IValidationDictionary {
public ModelStateWrapper(ModelStateDictionary msd){}
}
public class CustomerService: ICostumerService{
public IValidationDictionary ValidationDictionary { get; set; }
public CustomerService(ICustomerRepsitory customerRepository, IValidationDictionary validationDictionary ){}
public CustomerService(ICustomerRepsitory customerRepository){}
}
public Controller(ICustomerService customerService)
{
_customerService= menuService;
_customerService.ValidationDictionary = new ModelStateWrapper(this.ModelState);
_customerService= sportsService;
}

Resources