Use embedded neo4j with neo4j client - java

I can connect my neo4j server using the neo4jClient and every thing works fine.
For unit testing scenarios I want to use a different local server to perform unit testing to my neo4j DAL layer.
so I tried the neo4j embedded version. I can create nodes and query them using the deprecated
GraphDatabaseService graphDb = new GraphDatabaseFactory().newEmbeddedDatabase(DB_PATH)
ExecutionEngine engine = new ExecutionEngine(graphDb);
1)What is the new way to create embedded neo4j instance?
2)How can I query the embedded using the neo4jClient? tried connecting with local host but with no success(is the embedded version has web host?)

What is the new way to create embedded neo4j instance?
You actually already did it with the code in your question!
The documentation on the hello world app for embedded neo4j shows this code:
graphDb = new GraphDatabaseFactory().newEmbeddedDatabase( DB_PATH );
So you're already there.
How can I query the embedded using the neo4jClient? tried connecting with local host but with no success(is the embedded version has web host?)
If by the "neo4jclient" you mean the tool that people use in their browsers to visualize graphs, here's how to do that.
When you create an embedded neo4j database, the DB_PATH is important. Basically you just end up creating a directory by that name locally.
The neo4j browser application can be pointed at any graph path. It doesn't run embedded, it runs along with the server, so practically speaking what you'll do is configure the server to point to that directory you created for the embedded DB, and then it'll work.
See this documentation, you need to set:
org.neo4j.server.database.location=data/graph.db
Where data/graph.db is the same as DB_PATH in your embedded example.

Check with this example, it will help you.
Application.java
package hello;
import java.io.File;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.kernel.impl.util.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.core.GraphDatabase;
#SpringBootApplication
public class Application implements CommandLineRunner {
#Configuration
#EnableNeo4jRepositories(basePackages = "hello")
static class ApplicationConfig extends Neo4jConfiguration {
public ApplicationConfig() {
setBasePackage("hello");
}
#Bean
GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory().newEmbeddedDatabase("C:/neo4j-community-2.1.7/data/graph.db");
}
}
#Autowired PersonRepository personRepository;
#Autowired GraphDatabase graphDatabase;
public void run(String... args) throws Exception {
Person greg = new Person("Greg");
Person roy = new Person("Roy");
Person craig = new Person("Craig");
Person abc=new Person("ABC");
Person def=new Person("DEF");
Person ghi=new Person("GHI");
/*System.out.println("Before linking up with Neo4j...");*/
for (Person person : new Person[] { greg, roy, craig,abc,def,ghi }) {
/* System.out.println(person);*/
}
Transaction tx = graphDatabase.beginTx();
try {
personRepository.save(greg);
personRepository.save(roy);
personRepository.save(craig);
personRepository.save(abc);
personRepository.save(def);
personRepository.save(ghi);
greg = personRepository.findByName(greg.name);
greg.worksWith(roy);
greg.worksWith(craig);
personRepository.save(greg);
roy = personRepository.findByName(roy.name);
roy.worksWith(craig);
// We already know that roy works with greg
personRepository.save(roy);
// We already know craig works with roy and greg
// System.out.println("Lookup each person by name...");
for (String name : new String[] { greg.name, roy.name, craig.name }) {
System.out.println("--->"+personRepository.findByName(name));
}
// System.out.println("Looking up who works with Greg...");
for (Person person : personRepository.findByTeammatesName("Greg")) {
System.out.println("==>>"+person.name + " works with Greg.");
}
tx.success();
} finally {
tx.close();
}
}
public static void main(String[] args) throws Exception {
FileUtils.deleteRecursively(new File("C:/neo4j-community-2.1.7/data/graph.db"));
SpringApplication.run(Application.class, args);
}
}
create a pojo file, Person.java
package hello;
import java.util.HashSet;
import java.util.Set;
import org.neo4j.graphdb.Direction;
import org.springframework.data.neo4j.annotation.Fetch;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.annotation.RelatedTo;
#NodeEntity
public class Person {
#GraphId Long id;
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
#RelatedTo(type="TEAMMATE", direction=Direction.BOTH)
public #Fetch Set<Person> teammates;
public void worksWith(Person person) {
if (teammates == null) {
teammates = new HashSet<Person>();
}
teammates.add(person);
}
public String toString() {
String results = name + "'s teammates include\n";
if (teammates != null) {
for (Person person : teammates) {
results += "\t- " + person.name + "\n";
}
}
return results;
}
}
and create PersonRepository.java
package hello;
import org.springframework.data.repository.CrudRepository;
public interface PersonRepository extends CrudRepository<Person, String> {
Person findByName(String name);
Iterable<Person> findByTeammatesName(String name);
}

Related

Insert spring boot data to database

I am completely new to spring boot and I am now trying to insert some data to my database from spring boot. What is the correct way to do this?
file structure
NewUser.java
package com.example.demo.pojo;
public class NewUser {
private String CompanyName;
public String getCompanyName() {
return CompanyName;
}
public void setCompanyName(String CompanyName) {
this.CompanyName = CompanyName;
}
}
RegistrationController.java
package com.example.demo.controller;
import com.example.demo.result.Result;
import com.example.demo.pojo.NewUser;
import org.springframework.beans.factory.annotation.Autowired;
import com.example.demo.service.RegistrationService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
#RestController
public class RegistrationController {
#CrossOrigin
#PostMapping(value = "api/registration")
#ResponseBody
public Result registration(#RequestBody NewUser user) {
System.out.println(user.toString());
return new Result(200);
}
}
Above is how I get data from frontend and below is what I tried to insert data. How should I call the service to insert data?
AccApplMapper.java
package com.example.demo.mapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
#Mapper
public interface AccApplMapper {
#Insert("INSERT INTO ACCT_APPL(ENG_COMP_NAME) VALUES(#{CompanyName}")
public int addAcctAppl(#Param("CompanyName") String CompanyName);
}
RegistrationService.java
package com.example.demo.service;
import com.example.demo.mapper.AccApplMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class RegistrationService {
private AccApplMapper accApplMapper;
public int addAcctAppl(String CompanyName) {
return accApplMapper.addAcctAppl(CompanyName);
}
}
Based on question above, you can modify your registration method in RegistrationController something like below, along with using Autowired annotation in controller :
public class RegistrationController {
..
#Autowired
RegistrationService registrationService;
...
public Result registration(#RequestBody NewUser user) {
System.out.println(user.toString());
if(user!=null && user.getCompanyName()!=null) {
int insert = registrationService.addAcctAppl(user.getCompanyName());
return insert>0 ? new Result(200) : new Result(500);
}
else {
return new Result(400);
}
}
here based on input data, calling service method & returning appropriate httpStatus code as argument to Result.
Hello friend I suggest you use Spring Data JPA dependency, it makes alot easier to perform any database operation.
Spring Data JPA provides repository support for the Java Persistence
API (JPA). It eases development of applications that need to access
JPA data sources.
Here are some good reference links
Spring Data JPA - Reference Documentation
Introduction to Spring Data JPA

Spring Boot: Redis CRUD Repository findById or findAll always returns Optional.empty (null)

Hi Team,
I am using Spring Boot 2.3.12.RELEASE which internally uses Spring Data Redis 2.3.9.RELEASE as a managed dependency.
When I am trying to save an object to the Redis cache using Spring Boot CRUD repository, it is getting stored without any error and I can see the object stored via Redis Manager.
However, when I try to fetch the same object using the same id i.e. using findById() method of CRUD repository, I am unable to find it.
Moreover, when I try findAll() on the same CRUDRepository object I get Optional.empty result which is strange as findAll() should return all records present in the repository.
I have added the configuration, repository and model class codes and some screenshots below for your perusal.
Please Note: I know there are many similar questions asked on this platform related to this issue and also I tried the solutions mentioned on such questions, but that didn't work for me.
Any solutions for this issue will be really helpful.
Model Class:
package com.test.cache.entity;
import java.util.concurrent.TimeUnit;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.TimeToLive;
import org.springframework.data.redis.core.index.Indexed;
import lombok.AllArgsConstructor;
import lombok.Data;
#Data
#AllArgsConstructor
#RedisHash("OTPValidationLogCache")
public class OTPValidationLogCache {
#Id
#Indexed
private String id;
#Indexed
private int validationFailureCount;
#TimeToLive(unit = TimeUnit.MILLISECONDS)
private long expiry;
}
Repository:
package com.test.cache.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.test.cache.entity.OTPValidationLogCache;
#Repository
public interface OTPValidationLogCacheRepository extends CrudRepository<OTPValidationLogCache, String> {
}
Redis Configuration Class:
package com.test.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import java.time.Duration;
#Configuration
#EnableRedisRepositories(basePackages = "com.test")
public class RedisConfig {
public static final long REDIS_CONNECT_TIMEOUT_SECS = 10L;
#Bean
public RedisStandaloneConfiguration redisStandaloneConfiguration() {
final RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName("*******");
redisStandaloneConfiguration.setPort(6379);
redisStandaloneConfiguration.setPassword(RedisPassword.of("**********"));
//Credentials hidden for code sharing purpose.
return redisStandaloneConfiguration;
}
#Bean
public JedisConnectionFactory redisConnectionFactory() {
final JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder()
.connectTimeout(Duration.ofSeconds(REDIS_CONNECT_TIMEOUT_SECS))
.useSsl()
.build();
return new JedisConnectionFactory(redisStandaloneConfiguration(), jedisClientConfiguration);
}
#Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory());
return template;
}
}
Redis Manager Screenshot:
Eclipse IDE - Screenshot of Debugging Screen:
Well, I also raised a defect to spring-data-redis repository on GitHub for the same but the defect got closed by one of the maintainers of this repository without even posting any proper solution. He just gave a reference to an existing issue that was even closed without posting any solution. Here is the link to that issue.
https://github.com/spring-projects/spring-data-redis/issues/2130
Hence, while doing some research, I came across a solution that I am sharing here which worked in my case.
The solution is not to use the default CRUD repository methods implemented by Spring Boot, instead, write your own repository class having methods with your criteria to store and fetch the data from the Redis cache. That's it, now you should be able to store/fetch the data using the repository methods across your project.
I am posting an example below for reference.
Custom Repository Interface
package com.test.cache.repository;
import java.io.IOException;
import java.util.Map;
import com.test.cache.entity.OTPValidationLogCache;
public interface OTPValidationLogCacheRepository {
void save(OTPValidationLogCache customer);
OTPValidationLogCache find(Long id);
Map<?,?> findAll() throws IOException;
void update(OTPValidationLogCache customer);
void delete(Long id);
}
Custom Repository Interface Implementation
package com.test.cache.repository;
import java.io.IOException;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ScanOptions.ScanOptionsBuilder;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.test.cache.entity.OTPValidationLogCache;
import com.test.configuration.AppConfig;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
#Repository
public class OTPValidationLogCacheRepositoryImpl implements OTPValidationLogCacheRepository {
private String key;
private RedisTemplate redisTemplate;
private HashOperations hashOperations;
private ObjectMapper objMapper;
#Autowired
public OTPValidationLogCacheRepositoryImpl(RedisTemplate redisTemplate, ObjectMapper objmapper) {
this.redisTemplate = redisTemplate;
this.objMapper = objmapper;
}
#PostConstruct
private void init() {
hashOperations = redisTemplate.opsForHash();
}
#Override
public void save(OTPValidationLogCache otpvalCache) {
hashOperations.put(key.concat(otpvalCache.getId().toString()), otpvalCache.getId(), otpvalCache);
setExpiryTime(key.concat(String.valueOf(otpvalCache.getId())), AppConfig.getUserBanDurationInSeconds());
}
#Override
public OTPValidationLogCache find(Long id) {
return (OTPValidationLogCache) hashOperations.get(key.concat(String.valueOf(id)), id);
}
#Override
public Map findAll() throws IOException {
Map<Integer, OTPValidationLogCache> values = Maps.newHashMap();
Cursor c = hashOperations.scan(OTPValidationLogCache.class, new ScanOptionsBuilder().match(key.concat("*")).build());
AtomicInteger count = new AtomicInteger(1);
c.forEachRemaining(element ->
{
values.put(count.getAndIncrement(), objMapper.convertValue(element, OTPValidationLogCache.class));
}
);
c.close();
return values;
}
#Override
public void update(OTPValidationLogCache customer) {
hashOperations.put(key, customer.getId(), customer);
}
#Override
public void delete(Long id) {
hashOperations.delete(key, id);
}
private void setExpiryTime(String key, Long timeout)
{
redisTemplate.expire(key, Duration.ofSeconds(timeout));
}
public synchronized void setKey(String key)
{
this.key = key;
}
}
Hope this helps others who may encounter this issue in the future.
Also, there is one more alternative available for this issue, that is switching to a different library provider such as Redisson, however, I have not tried it yet, so if you want, you may try and check.
You need to have the same package for your entities ,
I resolved the problem by extracting a lib and putting my entities there
You would find an explication here :
https://github.com/spring-projects/spring-data-redis/issues/2114

Spring Boot Data console application

I'm gonna create a Java console application for accessesing a database (MySQL). I'm gonna use Spring Boot/Spring Data JPA. What is the correct way to create a console application using Spring Boot?
I found a few ways to do this:
spring.main.web-application-type=NONE (in application.properties)
spring.main.web-environment = false (in application.properties)
using the Spring Shell project
implementing the CommandLineRunner interface
I suppose that some of them may be obsolete, have pros and cons. Could you please explain how to create a plain console application using Spring Boot/Spring Data?
Recently, I have done a console application, as you require now. I did that by implementing CommandLineRunner interface. When spring boot starts the application, it will invoke the run(String... args) method of CommandLineRunner interface.
So, you can autowire(or using constructor injection) spring data repositories in this implemention class(e.g. AppRunner) & invoke database operations.
Instead of MySql database, I have you used MongoDB along with some caching operations.
Example:
AppRunner.java
package com.cache.caching;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
#Component
public class AppRunner implements CommandLineRunner {
Logger logger = LoggerFactory.getLogger(AppRunner.class);
BookRepository bookRepository;
public AppRunner(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
#Override
public void run(String... args) throws Exception {
logger.info("articles fetching..."+bookRepository.getArticles());
logger.info("articles fetching..."+bookRepository.getArticles());
logger.info("articles fetching..."+bookRepository.getArticles());
logger.info("articles fetching..."+bookRepository.getArticles());
}
}
BookRepository.java
package com.cache.caching;
import java.net.UnknownHostException;
import java.util.List;
public interface BookRepository {
List<Article> getArticles() throws UnknownHostException;
}
BookRepositoryImpl.java
package com.cache.caching;
import com.mongodb.*;
import org.bson.codecs.pojo.annotations.BsonId;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.mongodb.MongoCollectionUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
#Component
public class BookRepositoryImpl implements BookRepository{
#Override
#Cacheable("articles")
public List<Article> getArticles() throws UnknownHostException {
MongoClient mongoClient
= new MongoClient(new MongoClientURI("mongodb://localhost:27017"));
DB db = mongoClient.getDB("Mart");
DBCollection collection = db.getCollection("articles");
DBCursor cursor = collection.find();
List<Article> list= new ArrayList<>();
while (cursor.hasNext()) {
Article article = new Article();
DBObject dbObject = cursor.next();
article.setId((Double) dbObject.get("_id"));
article.setSubject((String) dbObject.get("subject"));
list.add(article);
}
return list;
}
}
In your case, you can provide MySQL database connection details here in application.yml / application.properties file.
CachingApplication.java - application starts here, this SpringApplication.run(CachingApplication.class, args); invokes the run(String... args) method of CommandLineRunner interface.
package com.cache.caching;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class CachingApplication {
public static void main(String[] args) {
SpringApplication.run(CachingApplication.class, args);
}
}
Sample: Sample Full Example Here

What is the table name?

I am creating a Spring Boot application using the H2 database. I am constantly getting the following error:
Table "THINGS_TO_DO" not found; SQL statement:
insert into things_to_do (id, name, verified) values (1, 'TestUser1', 1) [42102-197]
And, I feel this is logical since I don't know where to pass this table name in the application. Also, what should the table name be - is there some specific name that the table must have?
My ThingsToDo.java is like below:
package me.hiboy.springboot.microservice.example.todo;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="things_to_do")
public class ThingsToDo {
#Id
private Long id;
#Column(name="name")
private String name;
#Column(name="verified")
private int verificationStatus;
private String task;
public ThingsToDo() {
}
public ThingsToDo(Long id, String name, int verificationStatus, String task) {
super();
this.id=id;
this.name=name;
this.verificationStatus=verificationStatus;
this.task=task;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public int getVerificationStatus() {
return verificationStatus;
}
public String getTask() {
return task;
}
}
The controller ThingsToDoController.java is as follows:
package me.hiboy.springboot.microservice.example.todo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class ThingsToDoController {
#Autowired
ThingsToDoRepository repository;
#GetMapping("/")
public String index() {
return "Hello from the ToDo Controller\n";
}
#GetMapping("/todo/{name}")
public ThingsToDo getThingsToDo(#PathVariable String name) {
ThingsToDo thingToDo=repository.findByName(name);
return thingToDo;
}
}
Repository ThingsToDoRepository is:
package me.hiboy.springboot.microservice.example.todo;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ThingsToDoRepository extends JpaRepository<ThingsToDo, Long> {
ThingsToDo findByName(String name);
}
Application.properties is:
spring.application.name=todo-service
server.port=8080
spring.jpa.show-sql=true
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:mydb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.platform=h2
spring.datasource.initialize=true
data.sql is:
insert into things_to_do (id, name, verified) values (1, 'TestUser1', 1);
insert into things_to_do (id, name, verified) values (2, 'TestUser2', 0);
I don't think pom.xml is required - in case it is, kindly lemme know and I will post that as well. Thanks.
Edit:
The one with the main() method is here:
package me.hiboy.springboot.microservice.example.todo.springbootmicroservicetodoservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SpringBootMicroserviceTodoServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootMicroserviceTodoServiceApplication.class, args);
}
}
Edit: All the answers given so far do not help at all.
Follow the package name as
If your main class is in the com.example package
then all packages in your application will be following this package
as for new entity, the package will be
com.example.entity
You need to use proper package names as I can see you Application class and other classes do not follow the package naming convention.
Plus If I just want to insert simple test data I often implement a ApplicationRunner. Implementations of this interface are run at application startup and can use e.g. a autowired repository to insert some test data.
Your implementation would look like this:
#Component
public class DataLoader implements ApplicationRunner {
#Autowired
ThingsToDoRepository repository;
#Autowired
public DataLoader(ThingsToDoRepository repository) {
this.repository = repository;
}
public void run(ApplicationArguments args) {
repository.save(new ThingsToDo(1, 'TestUser1', 1));
repository.save(new ThingsToDo(2, 'TestUser2', 0));
}
}
You are getting this error because there is no table with such name.
You could try to add spring.jpa.hibernate.ddl-auto=create-drop to your .properties file. Then each time you run your app it should generate that table using your entity.
Or you need to create a table with name things_to_do manually and then when you run your app it should work. For this, you need to add
/src/main/resources/schema.sql
create table things_to_do
(
id integer not null,
/*all the rest columns */
);
Another thing is that for Spring to find your components (like repository or service etc.) it scans packages. And auto-configs scan the package where your main class is located and all the nested packages.
So if you don't want to set manually where your classes are located, you need to follow this structure!
Example:
my.main.package // here is your main class
my.main.package.entities // here are your entities
my.main.package.repositories // your repos
my.main.package.services // services
This is just an example, it does not mean you should provide the same names, just follow the convention. Hope it is clear what I mean by package structure :)
Usually, you would refer to your table by its entity name, not the actual table name. In this case it would be ThingsToDo.
But in this particular case, you're overriding this by giving your entity another name:
#Entity
#Table(name="things_to_do")
public class ThingsToDo {
That's why you should work with "things_to_do" in your query, or remove the name statement.

NoClassDefFoundError when making a query in spring-data-solr within a play framework application

I keep running into an issue when making a spring-data-solr query from within my play framework v2.3.8 application. The query seems to succeed but then the class loader spring is using can't find the class files for my data model classes and throws a NoClassDefFoundError. Here is some relevant code.
First the data model in package 'model':
package model;
import org.apache.solr.client.solrj.beans.Field;
import org.springframework.data.annotation.Id;
public class Theatre
{
#Id
String id;
#Field
String docType;
// other fields, getters, and setters omitted for brevity
}
Here is the repository:
package model;
import java.util.List;
import org.springframework.data.solr.repository.SolrCrudRepository;
public interface TheatreRepository extends SolrCrudRepository<Theatre, String>
{
List<Theatre> findByDocType(String doc__type);
void delete(Theatre deleted);
List<Theatre> findAll();
Theatre findOne(String id);
#SuppressWarnings("unchecked")
Theatre save(Theatre persisted);
}
The Application class in play framework:
package controllers;
import java.util.ArrayList;
import java.util.List;
import model.Theatre;
import model.TheatreRepository;
import play.*;
import plugins.solr.SolrPlugin;
import views.html.*;
public class Application extends Controller {
public static Result index() {
TheatreRepository repository = Play.application().plugin( SolrPlugin.class ).getTheatreRepository();
if( repository != null )
{
List<Theatre> allTheatres = null;
try
{
allTheatres = repository.findByDocType("theatre");
Logger.info( "List of theatres found: " + allTheatres.toString() );
for(Theatre theatre: allTheatres)
{
Logger.info( "This theatre is: " + theatre.toString() );
}
}
catch( NoClassDefFoundError | ClassNotFoundException e )
{
Logger.error( "Classpath: " + System.getProperty( "java.class.path" ) );
}
}
else
{
Logger.error( "TheatreRepository is NULL!" );
}
return ok(index.render("Your new application is ready."));
}
}
And the ApplicationContext for spring:
package plugins.solr;
import play.Plugin;
import model.TheatreRepository;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import play.Logger;
import play.Play;
import play.api.Application;
public class SolrPlugin extends Plugin
{
private TheatreRepository theatreRepository;
ApplicationContext ctx;
public SolrPlugin(Application app)
{
}
public TheatreRepository getTheatreRepository()
{
return theatreRepository;
}
#Override
public void onStart()
{
super.onStart();
Logger.info( "Solr plugin started" );
ctx = new AnnotationConfigApplicationContext(SolrConfig.class);
theatreRepository = ctx.getBean( TheatreRepository.class );
}
}
Here is the JavaConfig based spring configuration:
package plugins.solr;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.solr.core.SolrOperations;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.data.solr.repository.config.EnableSolrRepositories;
import org.springframework.data.solr.server.support.HttpSolrServerFactory;
import play.Play;
#ComponentScan
#Configuration
#EnableSolrRepositories(basePackages = { "model" })
public class SolrConfig
{
public SolrConfig()
{
}
#Bean
public SolrServer solrServer()
{
HttpSolrServerFactory factory = new HttpSolrServerFactory( new HttpSolrServer( Play.application().configuration().getString( "ems.solr.url" ) ) );
return factory.getSolrServer();
}
#Bean
public SolrOperations solrTemplate()
{
return new SolrTemplate( this.solrServer() );
}
}
So at run time Application.index() gets called when reloading the browser window and as a result the call to theatreRepository.findByDocType("theatre"); gets executed. This is the stack trace from that call:
java.lang.NoClassDefFoundError: model/Theatre
at model.Theatre_Instantiator_982rn.newInstance(Unknown Source) ~[na:na]
at org.springframework.data.convert.BytecodeGeneratingEntityInstantiator$EntityInstantiatorAdapter.createInstance(BytecodeGeneratingEntityInstantiator.java:193) ~[spring-data-commons-1.10.0.RELEASE.jar:na]
at org.springframework.data.convert.BytecodeGeneratingEntityInstantiator.createInstance(BytecodeGeneratingEntityInstantiator.java:76) ~[spring-data-commons-1.10.0.RELEASE.jar:na]
at org.springframework.data.solr.core.convert.MappingSolrConverter.read(MappingSolrConverter.java:127) ~[spring-data-solr-1.4.0.RELEASE.jar:na]
at org.springframework.data.solr.core.convert.MappingSolrConverter.read(MappingSolrConverter.java:119) ~[spring-data-solr-1.4.0.RELEASE.jar:na]
Caused by: java.lang.ClassNotFoundException: model.Theatre
at java.lang.ClassLoader.findClass(ClassLoader.java:531) ~[na:1.7.0_80]
at java.lang.ClassLoader.loadClass(ClassLoader.java:425) ~[na:1.7.0_80]
at java.lang.ClassLoader.loadClass(ClassLoader.java:358) ~[na:1.7.0_80]
at model.Theatre_Instantiator_982rn.newInstance(Unknown Source) ~[na:na]
at org.springframework.data.convert.BytecodeGeneratingEntityInstantiator$EntityInstantiatorAdapter.createInstance(BytecodeGeneratingEntityInstantiator.java:193) ~[spring-data-commons-1.10.0.RELEASE.jar:na]
What I have noticed is that the model.Theatre class files are in play frameworks class loader but not in the system class loader. However, this doesn't seem like it should be a problem because spring was able to find the TheatreRespoitory class file when it needed to instantiate an instance of that class. I figure there must be something simple that I'm doing wrong but after trying a few things and doing research I'm still at a loss.
I did find the following post but it doesn't seem to be relevant since it is referencing a difference between versions of spring-data-mongodb.
MongoDB NoClassDefFoundError
Also, I just tried using the play frameworks class loader in the spring application context by doing the following in SolrPlugin.onStart()
ctx = new AnnotationConfigApplicationContext(SolrConfig.class);
((AnnotationConfigApplicationContext)ctx).setClassLoader( Play.application().classloader() );
This resulted in the same NoClassDefFoundError and stack trace. I also tried making the query in this same function, right after setting the class loader, and received the same error again.
A workaround is to force ReflectionEntityInstantiator in MappingMongoConverter which reverts to pre 1.7.0.RELEASE instantiation strategy based on reflection. For example:
mappingConverter.setInstantiators(
new EntityInstantiators(ReflectionEntityInstantiator.INSTANCE));

Categories