Good afternoon fellow coders!
I have spent the last hour looking to delete a single document from my mongo "testCollection". I would like to make use of the MongoRepository delete / deleteAll methods. However, it does not remove the document. It persists regardless of how many times I run the test class method. No errors are reported and the user has readWrite permissions in the database. I am able to run the mongo command to remove the newly created test document.
I have read about using the mongo template and create it for the deletion to be performed. I'm happy to do that but I would rather keep it as simple as possible.
import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoId;
#Data
#Document(collection = "testCollection")
public class TestClass {
#MongoId
private String id;
private String name;
public TestClass(String name) {
this.name = name;
}
}
Test Class Mongo Repository interface
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
public interface TestClassRepository extends MongoRepository<TestClass, String> {
public List<TestClass> findAllByName(String name);
public void deleteAllByIdIn(List<TestClass> list);
}
Test Method
import org.junit.Assert;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
#RunWith(SpringRunner.class)
#TestPropertySource(value = {"classpath:application.properties"})
#AutoConfigureMockMvc
public class testClassTest {
#Autowired
private TestClassRepository testClassRepository;
#Test
public void crudTest() {
TestClass testObj = new TestClass("Test");
testClassRepository.save(testObj);
List<TestClass> testClassList = testClassRepository.findAllByName("Test");
Assert.assertEquals(1, testClassList.size());
TestClass test = testClassList.get(0);
testClassRepository.deleteAllByIdIn(testClassList);
// Fails this assertion: Found 1, expected 0.
Assert.assertEquals(0, testClassRepository.findAllByName("Test").size());
}
}
As anyone else experienced a similar issue? If so, how'd you go about resolving it?
Thanks!
Additions to original post:
Here is the mongo query generated by the MongoRepository. It appears that it is not actually adding the "remove" mongo command to the query. Query: { "name" : "Test"}, Fields: {}, Sort: {}
With a stroke of dumb luck I managed to figure out the issue. The problem was with the type of identifier annotation I was using. This explanation from another stackoverflow user (What is use of #MongoId in Spring Data MongoDB over #Id?) had me revisit this aspect of the model.
I switched the identifier annotation from #MongoId to #Id. Since I have both JPA and MongoDB annotations I needed to make sure I chose the one from the org.springframework.data.annotation package rather than the javax.persistance package.
Hope this explanation helps others!
Related
I'm trying to produce a first test with Mockito in a Spring Boot Application.
Here is the complete class:
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Optional;
import static org.mockito.Mockito.doReturn;
#ExtendWith(MockitoExtension.class)
public class ObjectServiceTest {
#Mock
private ObjectRepository objectRepository;
#Mock
private ObjectService objectService;
#Test
#DisplayName("Should find object by objNumero with its service")
void shouldFindObjectByObjNumero() {
Object object = new Object("00000271");
doReturn(Optional.of(object)).when(objectRepository).findObjectByObjNumero("00000271");
Optional<Object> returnedObject = objectService.findObjectByObjNumero("00000271");
Assertions.assertTrue(returnedObject.isPresent(), "Object was found");
Assertions.assertSame(returnedObject.get(), object, "The returned object wasn't the same as the mock");
}
}
And here is the error message:
org.opentest4j.AssertionFailedError: Object was not found ==>
Expected :true Actual :false
As you can see, the test is supposed to pass because I've created a new object and I use the same reference each time!
So, I've the solution. I've made 2 mistakes with the service:
#Mock
private ObjectService objectService;
Here I need the ObjectServiceImpl and not the interface. Than, I've use #InjectMocks instead of #Mock. For the moment, I already don't know what is the difference but it works!
#InjectMocks
private ObjectServiceImpl objectServiceImpl;
See below spring boot code
I have used JPA repository.
Controller.
Service.
Repository
BaseController
package com.controller;
import com.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class BaseController {
#Autowired
private StudentService studentService;
#GetMapping(value = "/addStudent", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<String> base() {
studentService.save();
return new ResponseEntity<String>("SUCCESS", HttpStatus.OK);
}
}
StudentService.java
package com.service;
import com.model.Student;
import com.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
#Service("studentService")
public class StudentServiceImpl implements StudentService {
#Autowired
private StudentRepository studentRepository;
#Override
#Transactional
public Student save() {
Student student = new Student();
student.setFirstName("ABC");
student.setLastName("PQR");
studentRepository.save(student);
int i = 10 / 0; //Error code
return student;
}
}
StudentRepository
package com.repository;
import com.model.Student;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository("studentRepository")
public interface StudentRepository extends CrudRepository<Student, Long> {
public List<Student> findAll();
}
Application.properties
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
#maximum number of milliseconds that a client will wait for a connection
spring.datasource.hikari.connection-timeout = 20000
#minimum number of idle connections maintained by HikariCP in a connection pool
spring.datasource.hikari.minimum-idle= 10
#maximum pool size
spring.datasource.hikari.maximum-pool-size= 10
#maximum idle time for connection
spring.datasource.hikari.idle-timeout=10000
# maximum lifetime in milliseconds of a connection in the pool after it is closed.
spring.datasource.hikari.max-lifetime= 1000
#default auto-commit behavior.
spring.datasource.hikari.auto-commit =false
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.jdbcUrl=jdbc:mysql://localhost:3306/demo?autoReconnect=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.show-sql=true
spring.jpa.properties..hibernate.format_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.ddl-auto=update
After executing save method from StudentRepository, data get inserted
immediately into database. no rollback or any other isolation levels are
working in StudentServiceImpl.java even if Error code is there.
I have tried to set "spring.datasource.hikari.auto-commit =true" setting value true, Placed #Transaction at top of the StudentServiceImpl.java class but still it didn't worked.
You do not need to mess with any hikari settings, and certainly not with autoCommit(true) as this DISABLES transactions. Delete all these properties.
Where is the "error" in your code? Spring rolls back on unchecked exceptions being thrown (not checked ones or errors), I cannot see that in your code.
What behavior do you expect? It looks fine to me.
It's probably beacause of Open Jpa in View behaviour.
Write the following line in your properties file:
spring.jpa.open-in-view=false
take a look at this if you want to know more.
I have created the basic application using Spring boot using JPA. I have added #AutoWired annotation for RatingRepo in RatingResource, but haven't added #Component annotation to RatingRepo
package com.example.demo;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.RatingsRateService.model.Rating;
import com.example.demo.RatingsRateService.model.UserRating;
#RestController
#RequestMapping("ratingsdata")
public class RatingResource {
#Autowired
RatingRepo repo;
/*
* #RequestMapping("/{movieId}") public Rating
* getRating(#PathVariable("movieId") String movieId) { return new
* Rating(movieId,7); }
*/
#RequestMapping("users/{userid}")
public UserRating getRating(#PathVariable("userid") int userid) {
List<Rating> ratings =repo.findByUserId(userid);
/*
* List<Rating> ratings = Arrays.asList(new Rating("1",4), new Rating("2",3),
* new Rating("3",2));
*/
System.out.println(ratings);
UserRating userRating = new UserRating();
userRating.setUserRating(ratings);
return userRating;
}
}
package com.example.demo;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import com.example.demo.RatingsRateService.model.Rating;
//to update the data in database , created the interd=face and will implement
//class,primary key
public interface RatingRepo extends JpaRepository<Rating, Integer>{
#Query(" from Rating where userid = ?1")
List<Rating> findByUserId( int userid);
}
. Still, it is working fine. Can you someone please explain why it is so? Or it is not needed to add #Component annotation for the repo?
first of there is #Repository annotation required not #Component
and #Repository also auto configure due to below:
Probably you are using spring boot
Spring Data repositories usually extend from the Repository or CrudRepository interfaces. If you are using auto-configuration, repositories will be searched from the package containing your main configuration class (the one annotated with #EnableAutoConfiguration or #SpringBootApplication) down.
ref: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html#boot-features-spring-data-jpa-repositories
I have the below code.
Note that I have an interface MySuperCoolEntityRepositoryContract.
And I have a "concrete interface" MySuperCoolEntityJpaRepository that implements my above MySuperCoolEntityRepositoryContract interface and JpaRepository.
All of that works fine with #ComponentScan.
I am changing my code to "java config", aka a centralized location where I can code up my DI definitions. (Also known as CompositionRoot in some circles).
The issue is when I try to "code up" the concrete for the interface. (Skip down to later in this question.
package com.me.domain.jpaentities;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
#Entity(name = "MySuperCoolEntityTableName")
public class MySuperCoolEntity implements Serializable {
#Id
#Column(name = "CoolSurrogateKeyColumn")
private String coolSurrogateKey;
#Column(name = "CoolMagicValueColumn")
private String coolMagicValue;
public String getCoolSurrogateKey() {
return this.coolSurrogateKey;
}
public void setCoolSurrogateKey(String coolSurrogateKey) {
this.coolSurrogateKey = coolSurrogateKey;
}
public String getCoolMagicValue() {
return this.coolMagicValue;
}
public void setCoolMagicValue(String coolMagicValue) {
this.coolMagicValue = coolMagicValue;
}
}
===============
package com.me.dal.repositories.interfaces;
import com.me.domain.jpaentities.MySuperCoolEntity;
import java.util.Collection;
public interface MySuperCoolEntityRepositoryContract {
Collection<MySuperCoolEntity> findByCoolMagicValue(String coolMagicValue);
}
=========================
package com.me.dal.repositories;
import com.me.dal.repositories.interfaces.MySuperCoolEntityRepositoryContract;
import com.me.domain.jpaentities.MySuperCoolEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Collection;
#Repository
public interface MySuperCoolEntityJpaRepository extends MySuperCoolEntityRepositoryContract, JpaRepository<MySuperCoolEntity,String> {
Collection<MySuperCoolEntity> findByCoolMagicValue(String coolMagicValue);
}
Now this issue.
package com.me.myapplication.configuration;
import com.me.dal.repositories.MySuperCoolEntityJpaRepository;
import com.me.dal.repositories.interfaces.MySuperCoolEntityRepositoryContract;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MyCompositionRoot {
#Bean
public MySuperCoolEntityRepositoryContract getAMySuperCoolEntityRepositoryContract()
{
return new MySuperCoolEntityJpaRepository(); /* << issue is here, this is an abstract class, aka, an interface with some methods defined */
}
}
Using the super cool JpaRepository "concrete interface" aka "really an abstract class but called an interface" aka "Interface Default Methods" ( see https://dzone.com/articles/interface-default-methods-java ) ........
The exact error is:
MySuperCoolEntityJpaRepository is abstract; cannot be instantiated
I do understand the error. MySuperCoolEntityJpaRepository is abstract. I get that.
But with this super cool "just extend JpaRepository and get all kinds of default functionality".....
How do I register a concrete JpaRepository with Spring DI (specifically with "code it up" java config ?
............
I tried making it a "class".
public class MySuperCoolEntityJpaRepository extends MySuperCoolEntityRepositoryContract, JpaRepository<MySuperCoolEntity,String>
but that wants me to define all those built in methods like "findAll",etc, etc.
Spring boot magically provides implementation for the methods defined in your interface. The #EnableJpaRepositories scans all packages below the package for interfaces extending JpaRepository and creates a Spring bean for it that is backed by an implementation of SimpleJpaRepository (spring data provides default imlpementations of CRUD repository through this class).
Your interface MySuperCoolEntityJpaRepository extends the interface MySuperCoolEntityRepositoryContract , but you only extend the JpaRepository on the interface MySuperCoolEntityJpaRepository which means spring will only provide the default implementations for methods in the interface MySuperCoolEntityJpaRepository . So try it like :
public interface MySuperCoolEntityRepositoryContract extends JpaRepository<MySuperCoolEntity,String>{
Collection<MySuperCoolEntity> findByCoolMagicValue(String coolMagicValue);
}
then extend this in your repository like :
#Repository
public interface MySuperCoolEntityJpaRepository extends MySuperCoolEntityRepositoryContract {
Collection<MySuperCoolEntity> findByCoolMagicValue(String coolMagicValue);
}
Related Post : how annotation #Repository in java spring work?
I figured out a workaround. I don't really like it, but I guess it works.
I also added MySuperCoolEntityBalServiceContract (you can get the idea from just the below), so you know why/how I need to have the getAMySuperCoolEntityRepositoryContract method in my CompositionRoot class below.
I'll leave this (not marked) as the answer in case someone else has a better way, or sees issue(s) with the below. I don't like the EntitiyManager work around, but it got things moving.
package com.me.myapplication.configuration;
import com.me.apicore.managers.MySuperCoolEntityBalService;
import com.me.apicore.managers.interfaces.MySuperCoolEntityBalServiceContract;
import com.me.dal.repositories.interfaces.MySuperCoolEntityRepositoryContract;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import javax.inject.Inject;
import javax.persistence.EntityManager;
#Configuration
public class MyCompositionRoot {
#Inject
private EntityManager entManager; /* part of the work around trick */
#Bean
public MySuperCoolEntityBalServiceContract getAMySuperCoolEntityBalServiceContract() {
return new MySuperCoolEntityBalService(this.getAMySuperCoolEntityRepositoryContract());
}
#Bean
public MySuperCoolEntityRepositoryContract getAMySuperCoolEntityRepositoryContract() {
//return new MySuperCoolEntityJpaRepository(); /* does not work. :( */
RepositoryFactorySupport factory = new JpaRepositoryFactory(entManager);
MySuperCoolEntityRepositoryContract repository = factory.getRepository(MySuperCoolEntityRepositoryContract.class);
return repository;
}
}
And I tweaked this (note the addition of the RepositoryDefinition annotation)
package com.me.dal.repositories.interfaces;
import com.me.domain.jpaentities.MySuperCoolEntity;
import org.springframework.data.repository.RepositoryDefinition;
import java.util.Collection;
#RepositoryDefinition(domainClass = MySuperCoolEntity.class, idClass = String.class)
public interface MySuperCoolEntityRepositoryContract {
Collection<MySuperCoolEntity> findByCoolMagicValue(String coolMagicValue);
}
I'm trying to create a test class for some of my services but I haven't been able to mock my DAOs, which are annotated as seen here:
import org.springframework.stereotype.Repository;
#Repository
public class TestDAO {
}
This is my test class
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
class ArchivoTest {
#Mock
public TestDAO testDAO;
#Test
public void test() {
System.out.print(testDAO == null);
}
}
When I run the test with JUnit archivoDAO is always null, and I don't know what to do about it or if I'm missing something. I'm a complete noob at mockito, but a half an hour search through google solved nothing, so I guess it's something not that obvious. Thanks in advance