I cannot save autowired properties when using Spring components with Neo4j.
I have defined an abstract entity class which includes the graph id:
#NodeEntity
public abstract class DBEntity {
#GraphId private Long id;
}
There is a data model with a parent and a child both defined as Spring components.
I have simplified the data model here, in reality it is a tree with several child nodes and levels:
#Component("DataModel")
#Scope("prototype")
public class DataModel extends DBEntity {
#Autowired private DataStructure dataStructure;
public TestDataStructure testDataStructure;
}
#Component("DataStructure")
#Scope("prototype")
public class DataStructure extends DBEntity {
...
}
For testing purposes, I have also defined a test data structure without Spring annotations:
public class TestDataStructure extends DBEntity {
private String test;
public TestDataStructure() {
this.test = "test";
}
}
Here is also the definition of my repository and the database service implementation:
#Repository
public interface DataModelRepository extends GraphRepository<DBEntity> {
DBEntity save(DBEntity entity);
}
public class DatabaseServiceImpl implements DatabaseService {
#Autowired private DataModelRepository modelRepository;
public void putModel(DBEntity entity) {
modelRepository.save(Entity);
}
}
Now, when I call putModel with an Spring-managed instance of DataModel, it will save the
DataModel instance and also an instance of TestDataStructure as nodes with 1:1 relationship. The instance of DataStructure and the relationship to the instance of DataModel will not be saved, although it is not null in the program.
How can I achieve to save the whole DataModel? Probably Spring interferes with the Neo4j mapping. I am not sure, why autowired properties might get lost in the cloning process or whatever Neo4j uses to get the data. Maybe someone can shed some light on this?
Related
I'm making a small program using Spring, Maven and Hibernate. The current goal is to use JpaRepository to interact with a Postgresql database. However, when I try to call for it to list all entries in a table within the database, it spits out a NullPointerException. Online resources vary in their implementation, so it's been hard for me to understand what goes wrong.
My application can be summarized as follows:
Javatest3Application.java (Outermost layer, handles communication)
#SpringBootApplication
#EnableAutoConfiguration
#EnableJpaRepositories
#RestController
public class Javatest3Application {
//---VARIABLES---
private JavatestService service_handler = new JavatestService();
//---PUBLIC---
public static void main(String[] args) {
SpringApplication.run(Javatest3Application.class, args);
}
#PostMapping("/login")
public ResponseEntity<String> Login(#RequestBody Map<String, String> json_map) {
//>>Read json_map for account_name and account_pwd
//Ask Service layer to log user in
Long session_id = this.service_handler.OpenSession(account_name, account_pwd);
//>>Construct response, blah blah...
}
}
JavatestService.java (Middle layer, manages repository interaction)
#Service
public class JavatestService {
//---VARIABLES---
#Autowired
private JavatestRepository repo;
//---PUBLIC---
public JavatestService() {}
public Long OpenSession(String in_name, String in_pwd) {
//Call database for credentials
List<JavatestUser> user_listings = this.repo.findAll(); //<- THIS FAILS
//>>Go though list, blah blah...
}
}
JavatestRepository.java (Bottom layer, interface extention)
#Repository
public interface JavatestRepository extends JpaRepository<JavatestUser, Long> {
//List<JavatestUser> findAll(); <- Don't think I need to add this. I believe its already in JpaRepository
}
JavatestUser.java (Bottommost layer, DTO class for database entry)
#Entity
#Table(name = "javatest_table", schema = "javatest_schema")
public class JavatestUser {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long account_id;
private String account_name;
private String account_pwd;
public JavatestUser(){
}
//>>Getter and Setter functions, blah blah...
}
So, as far as I have understood it, we cannot instance objects of an interface. However, when using Spring, the program itself creates classes that implement the interface, and then hands such a derived class back to us via the #Autowired keyword.
Thus when we call the findAll() function, we use that derived class to fetch objects of the associated #Entity class.
From my research I've come to believe I might use the #Service keyword wrong, and that it perhaps should be a #Controller. However, as far as I can see, the are implementations of both alternatives, so my understanding of what differentiates them is somewhat lacking. Regardless of which I am using, the #Autowired doesn't seem to provide any JavatestRepository-derived object for me to call findAll() upon.
EDITS
Added #EnableJpaRepositories in accordance with Eugene Botyanovsky's suggestion.
You are probably missing annotation, which enables all your repositories:
#EnableJpaRepositories
It's similar to #EnableAutoConfiguration you used, but exactly for JPA repositories.
This question already has answers here:
Spring JPA entities not saving to database
(3 answers)
Closed 2 years ago.
I have a server using spring boot and spring data jpa.
I have two classes annotated with #RestController in my server. One of them might change entities, while the other won't.
#RestController
#Slf4j
public class ControllerA {
private EntityRepository entityRepository;
public ControllerA(EntityRepository entityRepository) {
this.entityRepository = entityRepository;
}
#PostMapping("/pathStr")
public void changeEntity(#RequestParam("entityId") Long entityId) {
// init
Entity entity = entityRepository.findById(entityId);
// make changes to entity
entity.getOneToOneLinkedEntity().setIntProperty(0);
// save entity
entityRepository.save(entity);
}
}
#RestController
#Slf4j
public class ControllerB {
private Entity cachedEntity;
private EntityRepository entityRepository;
public ControllerB(EntityRepository entityRepository) {
this.entityRepository = entityRepository;
}
#MessageMapping("/otherPath")
public void getEntity(ArgumentType argument) {
if (cachedEntity == null) {
cachedEntity = entityRepository.save(new Entity());
}
Entity entity = entityRepository.findById(cachedEntity.getId()).orElse(null);
int properyValue = entity.getOneToOneLinkedEntity().getIntProperty(); // this is not zero
}
}
Here are the two entities and the repository:
#Entity
public class Entity implements Serializable {
#Id
#GeneratedValue private Long id;
#NotNull
#OneToOne(cascade=CascadeType.ALL)
private OneToOneLinkedEntity linkedEntity;
}
#Entity
public class OneToOneLinkedEntity implements Serializable {
#Id
#GeneratedValue private Long id;
#NotNull
private int intProperty = 0;
}
public interface EntityRepository extends JpaRepository<Entity, Long> {
}
I make a call to ControllerA.changeEntity from the client, and once that returns, I make another call to ControllerB.getEntity. The changes I made in the first call aren't shown in the second call, (and they're not in the database either if I query directly with sql) the int property has an old value. Even though I do the save only on the Entity and not on the linkedEntity, the CascadeType.ALL should make the the linked entity update too, right?
I tried adding an entityRepository.flush() after the save in ControllerA, but the issue still appears. What can I do? How can I make ControllerB get the correct value of intProperty?
This is what I have in application.properties:
spring.jpa.hibernate.ddl-auto=create
spring.datasource.url=jdbc:mysql://localhost:3306/db_name
spring.datasource.username=user
spring.datasource.password=pass
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.DefaultComponentSafeNamingStrategy
You should add #Transactional annotation to your method, so spring will handle the transaction and commit your changes.
This usually comes on the #Service class, but I see in your example that you do not have one, so put it on the controller (or add service layer, I think it's better)
You are ignoring dependency injection which is the beauty of spring framework.
Make a Repository class which implements JPARepository and annotate it with #Repository.
Make a Service class and annotate it with #Service and #Transactional.
In Service class autowire the Repository and make corresponding methods call like .save() .find() etc.
In Controller Class autowire the service class and call service method which will call repository methods.
This is all you have to do.
For making your application flow fast, better you create model for the entity class and pass the model between classes instead of entity as it contains a lot more information and thus much more heavy than normal model object.
I have a base class like (this is an artificial example):
#Document(collection = "cars")
public class BaseCar implements Serializable
{
private int number;
private String color;
...
}
Then I have a derived class like:
#Document(collection = "cars")
public class FastCar extends BaseCar implements Serializable
{
private int numberOfWonRaces;
...
}
For both I have a MongoRepository class:
public interface BaseCarRepository extends MongoRepository<BaseCar, String> {
{ ... }
and
public interface FastCarRepository extends MongoRepository<FastCar, String> {
{ ... }
If I now save a FastCar in MongoDB the I get additionally a _class field added which indicates from where the data is coming from. In this example it shows FastCar.
In my project I have a REST API interface to get cars. I use the findBy function to get a car by its color. For example:
BaseCar baseCar = baseCarRep.findByColor(color);
Even if I use an object of BaseCar, Springboot is detecting that it is a FastCar and is returning an FastCar object with all the information.
Question:
Is there a way to force Springboot to return only a BaseCar? I do not want to send all the information to the REST API interface.
What I have done so far:
If I remove the the _class field in the MongoDB, Springboot cannot automatically detect the class anymore and is returning the BaseCar. But I do not want to lose this functionality by forcing Springboot to remove the _class (Spring data MongoDb: MappingMongoConverter remove _class)
It seems that there is also a way with projections to filter the fields which should be returned. This is to me not an elegant way as I have to write down all the fields again and I have to update it as soon as I am updating the BaseCar class.
Thank you for any help.
Philipp
I got the following problem. I want to create SomeObject. This object consists of various nested objects NestedObject1, NestedObject2, ... I created mappers to create those nested objects Mapper1 to create NestedObject1, Mapper2 to create NestedObject2, and so on. Those Mappers call a huge amount of setters, and some of them need information from some entites from the db (and some don't). This is the problem in the java language:
public class MyClass {
#Inject
private MyDao dao;
#Inject
private Mapper1 mapper1;
#Inject
private Mapper2 mapper2;
#Inject
private Mapper3 mapper3;
#Inject
private Mapper4 mapper4;
#Inject
private Mapper5 mapper5;
public SomeObject map(Integer id) {
SomeEntity entity = dao.findById(id);
SomeObject someObject = new SomeObject();
someObject.setNestedObject1(mapper1.map(entity));
someObject.setNestedObject2(mapper2.map());
someObject.setNestedObject3(mapper3.map(entity));
someObject.setNestedObject4(mapper4.map(entity));
someObject.setNestedObject5(mapper5.map());
return someObject;
}
}
I am thinking of the following refactoring:
Make an interface Mapper and have all mappers implement this. Then I could inject the List of mappers. It would be pretty easy to add or remove on mapper, without touching MyClass. I think this is a good idea but the problem is the MyDao. Instead of one DB access I would then need 3.
The interface would then look like
public interface Mapper {
public void map(SomeObject someObject);
}
Mapper1 would look like
public class Mapper1 implements Mapper {
private static final Integer VALUTA = 1;
#Inject
private MyDao dao;
#Override
public void map(SomeObject someObject) {
SomeEntity entity = dao.findById(id); // and I have no idea what the id is
NestedObject1 nestedObject1 = new NestedObject1();
nestedObject1.setSomeField(entity.getSomething());
nestedObject1.setSomeOtherField(VALUTA);
someObject.setNestedObject1(nestedObject1);
}
}
id is unknown in this context. Include id in the signature? I have no idea...
Mapper3 and Mapper4 would have to look up the entity as well.
I was thinking about an abstract class which will look for the entity in the BeforeClass method, but I think this still get's called multiple times.
Btw: I know the title sucks, please feel free to rename it.
I'm trying to partially implement a repository using the following structure:
public interface ExampleCustomRepository {
Iterable<Example> findExamplesByUserId(Long id);
}
#Repository
#Transactional
public class ExampleCustomRepositoryImpl implements ExampleCustomRepository {
#Autowired
private Neo4jTemplate template;
#Override
public Iterable<Example> findExamplesByUserId(final Long id) {
// implementation
}
}
public interface ExampleRepository extends GraphRepository<Example>, ExampleCustomRepository {
}
For some reason the RepositoryFactory wants to create a DerivedGraphRepositoryQuery for this implemented method, and fails:
org.springframework.data.mapping.PropertyReferenceException: No property userId found for type Example!
Is it even possible to partially implement a repository with SDN4? If it is, what am I doing wrong?
I overlooked the naming convention, which was explained here.
I had too rename ExampleCustomRepositoryImpl to ExampleRepositoryImpl.