I have just started with Spring Data ElasticSearch. I have implemented my own repository, but I get a null pointer exception if I try to save an entity. I have got the following code, this is only some test code.
package org.test.elasticsearch.models;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
#Document(indexName = "test", type = "book", shards = 1, replicas = 0)
public class Book {
#Id
private String id;
private String title;
private String author;
public Book(final String id, final String title, final String author) {
this.id = id;
this.title = title;
this.author = author;
}
public String getId() {
return this.id;
}
public void setId(final String id) {
this.id = id;
}
public String getTitle() {
return this.title;
}
public void setTitle(final String title) {
this.title = title;
}
public String getAuthor() {
return this.author;
}
public void setAuthor(final String author) {
this.author = author;
}
}
package org.test.elasticsearch.configs;
import org.elasticsearch.node.NodeBuilder;
import org.test.elasticsearch.repositories.implementations.BookRepositoryImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
#Configuration
#EnableElasticsearchRepositories("org.test.elasticsearch.repositories")
public class ElasticsearchConfiguration {
#Bean
public ElasticsearchOperations elasticsearchTemplate() {
final NodeBuilder nodeBuilder = new NodeBuilder();
return new ElasticsearchTemplate(nodeBuilder.local(true).clusterName("elasticsearch").node().client());
}
#Bean
public BookRepositoryImpl bookRepositoryImplementation() {
return new BookRepositoryImpl();
}
}
package org.test.elasticsearch.repositories;
import org.test.elasticsearch.models.Book;
import org.springframework.data.elasticsearch.repository.ElasticsearchCrudRepository;
public interface BookRepository extends ElasticsearchCrudRepository<Book, String>, BookRepositoryCustom {
// query methods
}
package org.test.webapp;
import org.test.elasticsearch.models.Book;
import org.test.elasticsearch.models.Book.BookBuilder;
import org.test.elasticsearch.repositories.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Test {
#Autowired
static BookRepository BookRepository;
public static void main(final String[] args) {
SpringApplication.run(test.class, args);
final Book testBook = new Book("12345", "TestTitle", "TestAuthor");
BookRepository.save(testBook);
}
}
That's my code. And I get the following message after running my spring boot application.
Exception in thread "main" java.lang.NullPointerException
at org.test.webapp.test.main(Test.java:24)
Does anyone have an idea? And another question: When should I use ElasticsearchTemplate with IndexQuery over a custom repository to save my entities?
Your problem is, actually, with Spring Boot: you are not using it properly. Your Test class should look like this (no "static" access to BookRepository and done differently):
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.test.elasticsearch.models.Book;
import org.test.elasticsearch.repositories.BookRepository;
#Configuration
#ComponentScan(basePackages = {"org.test.elasticsearch.configs", "org.test.webapp"})
#EnableAutoConfiguration
public class Test implements CommandLineRunner {
#Autowired
private BookRepository bookRepository;
#Override
public void run(String... args) {
final Book testBook = new Book("12345", "TestTitle", "TestAuthor");
bookRepository.save(testBook);
}
public static void main(final String[] args) {
SpringApplication.run(Test.class, args);
}
}
Because, as I see it, you don't want to use a web application, so further you only need these dependencies in pom.xml. Nothing related to spring-boot-web:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
Related
package com.kk.config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#ComponentScan(basePackages="com.kk")
#EnableJpaRepositories(basePackages="com.kk.respositry")
#EntityScan(basePackages="com.kk.entity")
#SpringBootApplication
public class SpringBootEnumExampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootEnumExampleApplication.class, args);
}
}
package com.kk.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.kk.entity.Account;
import com.kk.service.AccountService;
#Controller
public class AccountController {
#Autowired
private AccountService accountService;
#RequestMapping(value="create",method=RequestMethod.POST)
private #ResponseBody String createAccout(#RequestBody Account account) {
Long l=accountService.save(account);
return "{\"accountId\":l}";
}
}
package com.kk.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import com.kk.enums.AccountRole;
#Entity
#Table(name = "account_tab")
public class Account {
#Id
#GeneratedValue
private Long id;
private String accountHolderName;
private String mobile;
private Integer age;
#Enumerated(EnumType.STRING)
#Column(name = "account_role", length = 40)
private AccountRole accountRole;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getAccountHolderName() {
return accountHolderName;
}
public void setAccountHolderName(String accountHolderName) {
this.accountHolderName = accountHolderName;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public AccountRole getAccountRole() {
return accountRole;
}
public void setAccountRole(AccountRole accountRole) {
this.accountRole = accountRole;
}
}
package com.kk.enums;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.kk.enums.utils.AccountRoleDeserializer;
#JsonDeserialize(using = AccountRoleDeserializer.class)
public enum AccountRole {
EMPLOYEE_CUSTOMER("Employee customer"),
JOINTER_ACSCOUNT("Jointer customer"),
PRIMARY_ACCOUNT("Primary customer"),
TENANT_ACCOUNT("Tenant customer");
private final String text;
AccountRole(final String text) {
this.text = text;
}
#Override
public String toString() {
return text;
}
public String getText() {
return this.text;
}
public static AccountRole fromText(String text) {
for (AccountRole r : AccountRole.values()) {
if (r.getText().equals(text)) {
return r;
}
}
throw new RuntimeException("Your AccountRole not valied: "+text );
}
}
package com.kk.enums.utils;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.kk.enums.AccountRole;
public class AccountRoleDeserializer extends JsonDeserializer<AccountRole> {
#Override
public AccountRole deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
if (node == null) {
return null;
}
String text = node.textValue(); // gives "A" from the request
if (text == null) {
return null;
}
return AccountRole.fromText(text);
}
}
package com.kk.respositry;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.kk.entity.Account;
#Repository
public interface AccountRespositry extends JpaRepository<Account, Long> {
}
package com.kk.service;
import com.kk.entity.Account;
public interface AccountService {
Long save(Account account);
}
package com.kk.service;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.kk.entity.Account;
import com.kk.respositry.AccountRespositry;
#Service
#Transactional
public class AccountServiceImpl implements AccountService{
#Autowired
private AccountRespositry accountRespositry;
#Override
public Long save(Account account) {
account=accountRespositry.save(account);
return account.getId();
}
}
server.port=8088
server.servlet.context-path=/SpringBootEnum/
## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url = jdbc:mysql://localhost:3306/Account?useSSL=false
spring.datasource.username = root
spring.datasource.password = root
## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
#Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update
I am using spring boot (data jpa ) but am getting the wrong value in the database.
The #Enumerated is behaving as expected. It's going to return the name of the enum and that's what gets persisted. Remember JPA uses the name() of the enum and not the toString() even if you have overridden the toString(). I would recommend using an AttributeConverter (JPA 2.1+) to control the persistence of your enum. In your case, create the converter to use the getText() method you already have defined in your Enum.
#Converter(autoApply = true)
public class AccountRoleConverter implements AttributeConverter<AccountRole, String> {
#Override
public String convertToDatabaseColumn(AccountRole role) {
return role.getText();
}
#Override
public AccountRole convertToEntityAttribute(String dbData) {
return AccountRole.fromText(dbData);
}
}
Note: #Converter(autoApply = true), tells JPA provider to use it to map all AccountRole enums.
Now you just need to make sure you remove the #Enumerated from your Account Entity:
#Enumerated(EnumType.STRING)
#Column(name = "account_role", length = 40)
private AccountRole accountRole;
becomes
#Column(name = "account_role", length = 40)
private AccountRole accountRole;
Ok you may ask how you use the converter. Well that is the nice part, you don't have to do anything. The persistence provider will use it for all read and write operations. I hope this helps.
I'm migrating our application from Spring Boot 1.5.9 to version 2.0.0.
In version 1.5.9 we have successfully used mixed Annotations on several Domain Classes e.g:
...
#org.springframework.data.mongodb.core.mapping.Document(collection = "folder")
#org.springframework.data.elasticsearch.annotations.Document(indexName = "folder")
public class Folder {
...
}
The same approach causes probems in Spring Boot 2.0.0. When MongoDB annotatnion #DBRef is used, Spring throws exception while ElasticsearchRepository creation:
java.lang.IllegalStateException: No association found!
Here comes classes and confs
pom.xml
...
<properties>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springfrsamework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
...
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
<scope>provided</scope>
</dependency>
</dependencies>
...
Application.java
...
#EnableMongoRepositories("com.hydra.sbmr.repoMongo")
#EnableElasticsearchRepositories("com.hydra.sbmr.repoElastic")
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Folder.java (Note this #DBRef couses exception)
package com.hydra.sbmr.model;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
#org.springframework.data.mongodb.core.mapping.Document(collection = "folder")
#org.springframework.data.elasticsearch.annotations.Document(indexName = "folder")
public class Folder {
#Id
#Getter #Setter private String id;
// Why MongoDB core mapping #DBRef causes java.lang.IllegalStateException: No association found! exception
// while ElasticsearchRepository creation???
#DBRef
#Getter #Setter private Profile profile;
#Getter #Setter private String something;
}
Profile.java
package com.hydra.sbmr.model;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.annotation.Id;
#org.springframework.data.mongodb.core.mapping.Document(collection = "profile")
public class Profile {
#Id
#Getter #Setter private String id;
#Getter #Setter String blah;
}
FolderElasticRepository.java
package com.hydra.sbmr.repoElastic;
import com.hydra.sbmr.model.Folder;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface FolderElasticRepository extends ElasticsearchRepository<Folder, String> {
}
You can find whole mini project on GitHub: https://github.com/hydraesb/sbmr
My question:
Is there any solution that will work with mixed Annotatnions on Domain Classes (mongo and elastic) in Spring Boot 2.0.0???
I have the same issue and the solution that i found is to extends SimpleElasticsearchMappingContext like this :
package com.mypackage;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.elasticsearch.client.Client;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.EntityMapper;
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.io.IOException;
#Configuration
public class ElasticsearchConfiguration {
#Bean
public ElasticsearchTemplate elasticsearchTemplate(Client client, Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder) {
return new ElasticsearchTemplate(client, new MappingElasticsearchConverter(new CustomElasticsearchMappingContext()),
new CustomEntityMapper(jackson2ObjectMapperBuilder.createXmlMapper(false).build()));
}
public class CustomEntityMapper implements EntityMapper {
private ObjectMapper objectMapper;
public CustomEntityMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
}
#Override
public String mapToString(Object object) throws IOException {
return objectMapper.writeValueAsString(object);
}
#Override
public T mapToObject(String source, Class clazz) throws IOException {
return objectMapper.readValue(source, clazz);
}
}
}
package com.mypackage;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchPersistentEntity;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder;
public class CustomElasticsearchMappingContext extends SimpleElasticsearchMappingContext {
#Override
protected ElasticsearchPersistentProperty createPersistentProperty(Property property, SimpleElasticsearchPersistentEntity owner, SimpleTypeHolder simpleTypeHolder) {
return new CustomElasticsearchPersistentProperty(property, owner, simpleTypeHolder);
}
}
package com.mypackage;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchPersistentProperty;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder;
public class CustomElasticsearchPersistentProperty extends SimpleElasticsearchPersistentProperty {
public CustomElasticsearchPersistentProperty(Property property, PersistentEntity owner, SimpleTypeHolder simpleTypeHolder) {
super(property, owner, simpleTypeHolder);
}
#Override
public boolean isAssociation() {
return false;
}
}
I have faced this problem also and I fixed with solution of #ybouraze
#Bean
fun elasticsearchTemplate(client: JestClient, converter: ElasticsearchConverter, builder: Jackson2ObjectMapperBuilder): ElasticsearchOperations {
val entityMapper = CustomEntityMapper(builder.createXmlMapper(false).build())
val mapper = DefaultJestResultsMapper(converter.mappingContext, entityMapper)
return JestElasticsearchTemplate(client, converter, mapper)
}
#Bean
#Primary
fun mappingContext(): SimpleElasticsearchMappingContext {
return MappingContext()
}
#Bean
fun elasticsearchConverter(): ElasticsearchConverter {
return MappingElasticsearchConverter(mappingContext())
}
inner class CustomEntityMapper(private val objectMapper: ObjectMapper) : EntityMapper {
init {
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
}
#Throws(IOException::class)
override fun mapToString(`object`: Any): String {
return objectMapper.writeValueAsString(`object`)
}
#Throws(IOException::class)
override fun <T> mapToObject(source: String, clazz: Class<T>): T {
return objectMapper.readValue(source, clazz)
}
}
inner class MappingContext : SimpleElasticsearchMappingContext() {
override fun createPersistentProperty(property: Property, owner: SimpleElasticsearchPersistentEntity<*>, simpleTypeHolder: SimpleTypeHolder): ElasticsearchPersistentProperty {
return PersistentProperty(property, owner, simpleTypeHolder)
}
}
inner class PersistentProperty(property: Property, owner: SimpleElasticsearchPersistentEntity<*>, simpleTypeHolder: SimpleTypeHolder) : SimpleElasticsearchPersistentProperty(property, owner, simpleTypeHolder) {
override fun isAssociation(): Boolean {
return false
}
}
I am working demo Spring boot application with integration of Redis.
I have referred various site reference but lastly I preferred to follow this: http://www.baeldung.com/spring-data-redis-tutorial
My code is almost same as given in above link. Only change is that I have autowired StudentRepository in my RestController class.
Now when I try to do maven-install at that time it gives me error that
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'studentController': Unsatisfied dependency expressed through field 'studentRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'studentRepositoryImpl' defined in file [/home/klevu/work/Nimesh/Spring Boot Workspace/bootDemo/target/classes/com/example/demo/redis/repository/StudentRepositoryImpl.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.example.demo.redis.repository.StudentRepositoryImpl]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: No visible constructors in class com.example.demo.redis.repository.StudentRepositoryImpl
When I tried to keep constructor public, it creates build successfully. But I don't know I should do it or not here. I was thinking that rather than Autowiring constructor, I should be able to do setter injection. I also tried below:
#Autowired
private RedisTemplate<String, Student> redisTemplate;
But it is also not working.
package com.example.demo.redis.repository;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;
import com.example.demo.redis.bean.Student;
#Repository
public class StudentRepositoryImpl implements StudentRepository {
private static final String KEY = "Student";
//#Autowired
private RedisTemplate<String, Student> redisTemplate;
private HashOperations<String, String, Student> hashOps;
#Autowired
private StudentRepositoryImpl(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
#PostConstruct
private void init() {
hashOps = redisTemplate.opsForHash();
}
#Override
public void saveStudent(Student person) {
hashOps.put(KEY, person.getId(), person);
}
#Override
public void updateStudent(Student person) {
hashOps.put(KEY, person.getId(), person);
}
#Override
public Student findStudent(String id) {
return hashOps.get(KEY, id);
}
#Override
public Map<String, Student> findAllStudents() {
return hashOps.entries(KEY);
}
#Override
public void deleteStudent(String id) {
hashOps.delete(KEY, id);
}
}
RedisConfiguration are default and code as below:
package com.example.demo.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
#Configuration
public class RedisConfiguration {
#Bean
JedisConnectionFactory jedisConnectionFactory() {
return new JedisConnectionFactory();
}
#Bean
public RedisTemplate<String, Object> redisTemplate(){
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(jedisConnectionFactory());
return template;
}
}
Spring boot main entry point is declared as below:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
#SpringBootApplication
#EnableMongoRepositories(basePackages = {"com.example.demo.mongo.repository"} )
#EnableRedisRepositories(basePackages = {"com.example.demo.redis.repository"})
public class BootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(BootDemoApplication.class, args);
}
}
Demo controller to test redis is as below:
package com.example.demo.controller;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.redis.bean.Student;
import com.example.demo.redis.repository.StudentRepository;
#RestController
#RequestMapping("/student")
public class StudentController {
#Autowired
private StudentRepository studentRepository;
#GetMapping
public ResponseEntity<Map<String, Student>> index() {
Map<String, Student> students = studentRepository.findAllStudents();
return new ResponseEntity<Map<String, Student>>(students, HttpStatus.OK);
}
#RequestMapping(method = RequestMethod.GET, value = "/{id}")
public ResponseEntity<Student> getStudentById(#PathVariable("id") String id) {
Student student = studentRepository.findStudent(id);
return new ResponseEntity<Student>(student, HttpStatus.OK);
}
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<Student> saveStudent(#RequestBody Student student) {
studentRepository.saveStudent(student);
return new ResponseEntity<Student>(student, HttpStatus.CREATED);
}
#RequestMapping(method = RequestMethod.PUT, value = "/{id}")
public ResponseEntity<Student> updateStudent(#RequestBody Student student) {
studentRepository.updateStudent(student);
return new ResponseEntity<Student>(student, HttpStatus.OK);
}
#RequestMapping(method = RequestMethod.DELETE, value = "/{id}")
public ResponseEntity<Student> deleteMessage(#PathVariable("id") String id) {
studentRepository.deleteStudent(id);
return new ResponseEntity<Student>(HttpStatus.OK);
}
}
You set the constructor as private... change it to public
#Autowired
public StudentRepositoryImpl(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
Change the following redis configuration:
Your original:
#Bean
public RedisTemplate<String, Object> redisTemplate() {
...
}
Change it to:
#Bean
public RedisTemplate<String, ?> redisTemplate(){
...
}
It should work now for you.
You could use Spring Data Redis
Add dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Enable Caching
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class RedisDemoApplication {
public static void main(String[] args) {
SpringApplication.run(RedisDemoApplication.class, args);
}
}
Add Cacheable annotation at method to cache in redis
#Cacheable(value = "employee", key = "#id")
public Employee getEmployee(Integer id) {
log.info("Get Employee By Id: {}", id);
Optional<Employee> employeeOptional = employeeRepository.findById(id);
if (!employeeOptional.isPresent()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Id Not foud");
}
return employeeOptional.get();
}
I just started with Spring Boot Data Neo4j and trying to finish the movie tutorial. I got this ERROR but not really sure how to debug. Error summary : (1) Error creating bean (2)Could not autowire field. Any help, thank you.
My files structure as follows :
SpringBootApplication.java
package com.test.springdataneothree;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.test.services.MovieService;
#EnableAutoConfiguration
#SpringBootApplication
public class SpringdataneothreeApplication implements CommandLineRunner {
#Autowired
MovieService movieService;
public static void main(String[] args) {
SpringApplication.run(SpringdataneothreeApplication.class, args);
}
#Override
public void run(String... arg0) throws Exception {
System.out.println("Main Spring Boot Class");
movieService.countMovie();
}
}
Movie.java
package com.test.movie;
import org.neo4j.ogm.annotation.GraphId;
import org.neo4j.ogm.annotation.NodeEntity;
#NodeEntity(label ="Movie")
public class Movie {
#GraphId
private Long id;
String title;
int released;
String tagline;
public Movie() { }
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getReleased() {
return released;
}
public void setReleased(int released) {
this.released = released;
}
public String getTagline() {
return tagline;
}
public void setTagline(String tagline) {
this.tagline = tagline;
}
}
MovieService.java
package com.test.services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.test.repositories.MovieRepository;
#Service("MovieService")
public class MovieService {
#Autowired
MovieRepository movieRepository;
public void countMovie() {
movieRepository.count();
}
}
MovieRepository.java
package com.test.repositories;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.GraphRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.test.movie.Movie;
#Repository
public interface MovieRepository extends GraphRepository<Movie> {
Movie findById(long id);
Movie findByTitle(#Param("title") String title);
#Query("MATCH (m:Movie) WHERE m.title =~ ('(?i).*'+{title}+'.*') RETURN m")
Collection<Movie> findByTitleContaining(#Param("title") String title);
#Query("MATCH (m:Movie)<-[:ACTED_IN]-(a:Person) RETURN m.title as movie, collect(a.name) as cast LIMIT {limit}")
List<Map<String,Object>> graph(#Param("limit") int limit);
}
MyNeo4jConfiguration.java
package com.test.configuration;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#EnableTransactionManagement
#EnableScheduling
#ComponentScan(basePackages = {"com.test"})
#Configuration
#EnableNeo4jRepositories(basePackages = "com.test.repositories")
public class MyNeo4jConfiguration extends Neo4jConfiguration {
#Override
public SessionFactory getSessionFactory() {
return new SessionFactory("com.test.movie");
}
}
You have a problem with auto wiring: the annotation on the MovieService is wrong, it should be:
#Service("movieService")
or just
#Service
You are trying to inject a bean with the capitalised name, and the context can't find it (the injection is done by name). Another solution is to use the #Qualifier annotation on the auto wired field, like
#Autowired
#Qualifier("MovieService")
MovieService movieService;
Hy,
Having these three classes I have found a problem:
package xpadro.spring.jms;
import javax.jms.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MarshallingMessageConverter;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
#SpringBootApplication
public class JmsJavaconfigApplication {
public static void main(String[] args) {
SpringApplication.run(JmsJavaconfigApplication.class, args);
}
#Bean
public Jaxb2Marshaller jaxb2Marshaller(){
Jaxb2Marshaller jaxb = new Jaxb2Marshaller();
Class array[] = new Class[1];
array[0]= xpadro.spring.jms.model.Order.class;
jaxb.setClassesToBeBound(array);
return jaxb;
}
#Bean
#Autowired
public MarshallingMessageConverter marshallingMessageConverter( Jaxb2Marshaller marshaller){
MarshallingMessageConverter mc = new MarshallingMessageConverter();
mc.setMarshaller(marshaller);
mc.setUnmarshaller(marshaller);
return mc;
}
#Bean
#Autowired
public JmsTemplate init(MarshallingMessageConverter messageConverter,ConnectionFactory connectionFactory){
JmsTemplate template = new JmsTemplate();
template.setMessageConverter(messageConverter);
template.setConnectionFactory(connectionFactory);
return template;
}
}
Next, the model:
package xpadro.spring.jms.model;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "order")
#XmlAccessorType(XmlAccessType.FIELD)
public class Order implements Serializable {
private static final long serialVersionUID = -797586847427389162L;
#XmlElement(required = true)
private String id="";
public Order() {
}
public Order(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
Next, the listener:
package xpadro.spring.jms.listener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import xpadro.spring.jms.model.Order;
import xpadro.spring.jms.service.StoreService;
#Component
public class SimpleListener {
private final StoreService storeService;
#Autowired
public SimpleListener(StoreService storeService) {
this.storeService = storeService;
}
#JmsListener(destination = "simple.queue")
public void receiveOrder(Order order) {
storeService.registerOrder(order);
}
}
The class that sends messages:
package xpadro.spring.jms.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;
import xpadro.spring.jms.model.Order;
#Service
public class ClientServiceImpl implements ClientService {
private static final String SIMPLE_QUEUE = "simple.queue";
private static final String IN_QUEUE = "in.queue";
private final JmsTemplate jmsTemplate;
#Autowired
public ClientServiceImpl(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
#Override
public void addOrder(Order order) {
//MessageRegistered with a bean
jmsTemplate.convertAndSend(SIMPLE_QUEUE, order);
}
#Override
public void registerOrder(Order order) {
//MessageRegistered with a bean
jmsTemplate.convertAndSend(IN_QUEUE, order);
}
}
The point is that listener(method receiveOrder()) is not executed when I set a custom message converter in the class JmsJavaconfigApplication. When I dont set it, spring boot sets a SimpleMessageConverter and the listener is executed.
Any wrong or missed configuration?
Thanks
You need to provide an marshalling converter to the #JmsListener to unmarshall the message, by configuring the listener container factory see the documentation.