I have a function createObject() in my service rest Service:
#Service
public class MyService {
//Repos and contructor
#Transactional
public ObjectDto createObject(Object) {
Mother mother = new Mother(name, age);
Kid kid = new Kid(name, age);
mother.addKid(kid);
this.motherRepo.saveAndFlush(mother);
Long kidId = kid.getId();
doStuffWithKidId();
return new ObjectDto()
.withMother(mother)
.withKid(kid)
.build();
}
}
My entity for mother/kid is basically like this:
#Entity
#Table("mother")
public class mother() {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id)
private Long id;
//other attributes, including #OneToMany for Kid
//Getter/Setter
}
There is a similar Entity for Kid.
As you can see, the id is set by the database. There is no setter for id in the entity. The constructor is without id as well.
Now I want to test my service. I mock my repos and want to verify, that my ObjectDto contains the values, like id.
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
public MyServiceTest {
#Mock
private MotherRepo motherRepo;
#InjectMocks
private MyService myService;
#Test
void createObjectTest() {
ObjectDto expectedObjectDto = setup...;
Object inputObject = setup...;
assertThat.(this.myService.createObject(inputObject))
.isEqualToComparingFieldByField(expectedObjectDto);
}
}
The expected ObjectDto looks something like
{
"motherId":1,
"motherAge":40,
"kidId":1
...
}
The problem is, that the id is set up by the database. Since there is no database and the repository is mock with Mockito, this value is always null. Even if I set my expectedObjectDto with null as id, I need the id in the "doStuffWithKidId()" in the service. Atm I get a NullPointerException.
Is there a possibility to set the id, like with ReflectionTestUtils.setField()? In literature I read, that a service should always be tested using mocks. Is this correct or do I need a in-memory db like H2?
Thanks for the help.
Use doAnswer...
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Java6Assertions.assertThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
#RunWith(MockitoJUnitRunner.class)
public class MockitoSettingDatabaseIds {
private static class TestEntity {
private long id;
private String text;
public TestEntity(String text) {
this.text = text;
}
public long getId() {
return id;
}
public String getText() {
return text;
}
}
private interface TestEntityDAO {
void save(TestEntity entity);
}
private static long someLogicToTest(TestEntityDAO dao, TestEntity entity) {
dao.save(entity);
return entity.getId();
}
#Test
public void shouldReturnDatabaseGeneratedId() {
long expectedId = 12345L;
TestEntityDAO dao = mock(TestEntityDAO.class);
TestEntity entity = new TestEntity("[MESSAGE]");
doAnswer(invocation -> {
ReflectionTestUtils.setField((TestEntity) invocation.getArgument(0), "id", expectedId);
return null;
}).when(dao).save(entity);
assertThat(someLogicToTest(dao, entity)).isEqualTo(expectedId);
}
}
To answer your comment, just do the same thing to the Kid collection, for example...
doAnswer(invocation -> {
Mother toSave = (Mother) invocation.getArgument(0);
ReflectionTestUtils.setField(toSave, "id", expectedId);
for (int k = 0; k < toSave.getKids().size(); k++) {
ReflectionTestUtils.setField(toSave.getKids().get(k), "id", expectedId + k + 1);
}
return null;
}).when(dao).save(entity);
This will set the id of the Mother to expectedId and the IDs of the Kids to expectedId + 1, expectedId + 2 etc.
Related
I have to write some junit test cases to check entity. I'm using postgres as my database.
My entity class
#Entity
#Table(name = "display")
public class Display {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
private String group;
public Display() {
}
public Display(Long id, String title, String grp) {
this.id = id;
this.title= title;
this.group= grp;
}
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return this.id;
}
public void setGroup(String id) {
this.group = id;
}
public String getGroup() {
return this.group;
}
public void settitle(String title) {
this.title = title;
}
public String gettitle() {
return this.title;
}
}
My repository
#Repository
public interface DisplayRepository extends CrudRepository<Display, Long> {
}
Interface
public interface IDisplayService {
List<Display> findAll();
}
Service class
#Service
public class DisplayService implements IDisplayService {
#Autowired
private DisplayRepository repository;
#Override
public List<Display> findAll() {
List<Display> d = (List<Display>) repository.findAll();
return d;
}
}
I tried writing junit test cases but I get Could'nt load Application. Whats the right way to write junit test cases for this?
This is the test case I wrote for service
folder : test/java/example/demo/Test.java
#RunWith(MockitoJUnitRunner.class)
#TestPropertySource("classpath:conn.properties")
public class DisplayServiceTest {
#Value("${id}")
private String value;
#Mock
private DisplayRepository DisplayReps;
#InjectMocks
private DisplayService DisplayService;
#Test
public void whenFindAll_thenReturnProductList() {
Menu m = new Menu()
m.setId(value);
List<Display> expectedDisplay = Arrays.asList(m);
doReturn(expectedDisplay).when(DisplayReps).findAll();
List<Display> actualDisplay = DisplayService.findAll();
assertThat(actualDisplay).isEqualTo(expectedDisplay);
}
in test/java/example/demo/resources
conn.properties
id=2
Its returning 0 for value
Whats the issue?
Thanks
I have managed to make your code to work. I will post only the changed classes:
The interface:
public interface DisplayRepository extends CrudRepository<Display, Long> {
Optional<Display> findByTitle(String name);
}
The test class:
#RunWith(SpringRunner.class)
#AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE)
#DataJpaTest
public class DisplayRepositoryTest {
#Autowired
private TestEntityManager testEntityManager;
#Autowired
private DisplayRepository productRespository;
#Before()
public void setUp(){
Display m = new Display();
// m.setId(2L); // The ID is autogenerated; can retrieve it from the persistAndFlush result
m.setCategory("Group1");
m.setTitle("Product2");
testEntityManager.persistAndFlush(m);
}
#Test
public void whenFindByName_thenReturnProduct() {
// when
Display product = productRespository.findByTitle("Product2").orElseThrow(() -> new RuntimeException("Product not found"));
// then
assertThat(product.getTitle()).isEqualTo("Product2");
}
#Test
public void whenFindAll_thenReturnProductList() {
// when
List<Display> products = (List<Display>) productRespository.findAll();
// then
assertThat(products).hasSize(1);
}
}
When trying to run the code you provided, there were a few issues:
you were using the reserved word group as a field in the Display class. Because of this, Hibernate couldn't create the table, so I renamed it to category.
there was a compilation issue because the method findByName wasn't defined in the repository; also, there was no field name in the Display class to which the mapping to be made; because of this, I've added the method findByTitle because it's an existing field and it seemed to match the value you queried in the test method.
because the ID field is autogenerated, the test setup() failed when persisting the Display.
If you want to use #Mock for mocking classes, you must call:
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
You can then mock responses as usual: Mockito.when(DisplayReps.findByTitle("A")).thenReturn(Optional.of(new Display(2L, "ALFA", "GRP1")));
I have a Spring Boot application in which I have created an entity, a repository and a service.
I save entities in the database via transactions and everything works fine, my database is populated as I would expect. Also, I should mention that my database is created in PHPMyAdmin.
I also created a repository in order to fetch some data from the database by extending the Crud Repository. I also have a service which stores the methods that call the repository.
Though, none of the methods I have return anything ( my database is not empty ) and I do not know why. I have also tried adding #EnableJpaRepositories and #ComponentScan for the entity, but this did not work. Below are my classes:
The entity ( I will not put all the getters and setters here) :
#Entity
#Table(name = "matches", schema = "tennis", catalog = "")
public class MatchesEntity {
private int id;
private String namePlayer1;
private String namePlayer2;
private int setsPlayer1;
private int setsPlayer2;
private String odd1;
private String odd2;
private String competition;
private String surface;
private String status;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Basic
#Column(name = "Name_player1")
public String getNamePlayer1() {
return namePlayer1;
}
public void setNamePlayer1(String namePlayer1) {
this.namePlayer1 = namePlayer1;
}
#Basic
#Column(name = "Name_player2")
public String getNamePlayer2() {
return namePlayer2;
}
// other getter & setters
}
The repository:
#Repository
public interface MatchesRepository extends CrudRepository<MatchesEntity,
Integer> {
List<MatchesEntity> getAllBySurface(String surface);
}
The service:
#Service
public class MatchesService {
#Autowired
MatchesRepository matchesRepository;
public int countMatchesOnHard() {
return matchesRepository.getAllBySurface("hard").size();
}
public MatchesEntity findMatchById() {
return matchesRepository.findById(2378).get();
}
}
The main class:
#SpringBootApplication
#EnableJpaRepositories(basePackageClasses={MatchesRepository.class})
#EntityScan(basePackageClasses=MatchesEntity.class)
public class PicksApplication {
#Autowired
static MatchesService matchesService;
public static void main(String[] args) {
MatchesEntity matchesEntity = matchesService.findMatchById();
int numberOfMatchesOnHard = matchesService.countMatchesOnHard();
System.out.println(numberOfMatchesOnHard);
}
}
Any method I try which is repository related returns null.
Can anyone help me with a suggestion ?
Your main class PicksApplication is troublesome. The main method must trigger SpringApplication.run for the spring boot to initialize itself & the context for autowires to work. You are breaking all that within your code. You can utilize CommandLineRunner and add your code in run() method.
Like this;
#SpringBootApplication
public class PicksApplication implements CommandLineRunner {
#Autowired
private MatchesService matchesService;
public static void main(String[] args) {
SpringApplication.run(PicksApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
MatchesEntity matchesEntity = matchesService.findMatchById();
int numberOfMatchesOnHard = matchesService.countMatchesOnHard();
System.out.println(numberOfMatchesOnHard);
}
}
then it ought to work, rest of the code looks OK
I've been following a lot of tutorial on how to get a list of result by referencing a specific column in the table.
I have this table.
I want to get the list of result with a plan_code "TEST123"
This is my code:
PlanRepository.java
public interface PlanCoverageRepository extends CrudRepository<PlanCoverage, Long> {
List<PlanCoverage> findAllByPlan_code(String plan_code);
}
PlanCoverageService.java
public interface PlanCoverageService {
public List<PlanCoverage> getAllPlanCoverageByPlanCode(String plan_code);
}
PlanCoverageServiceImpl.java
#Service
#Transactional
public class PlanCoverageServiceImpl implements PlanCoverageService {
#Override
public List<PlanCoverage> getAllPlanCoverageByPlanCode(String plan_code) {
return (List<PlanCoverage>) planCoverageRepository.findAllByPlan_code(plan_code);
}
}
PlanCoverageController.java
#Controller
#RequestMapping(value="/admin")
public class PlanCoverageController {
#Autowired
PlanCoverageService planCoverageService;
#RequestMapping(value="/Test/{plan_code}", method=RequestMethod.GET)
public ModelAndView test(#PathVariable String plan_code) {
ModelAndView model = new ModelAndView();
PlanCoverage planCoverage = (PlanCoverage) planCoverageService.getAllPlanCoverageByPlanCode(plan_code);
model.addObject("planCoverageForm",planCoverage);
model.setViewName("plan_coverage_form");
return model;
}
}
PlanCoverage.java
#Entity
#Table(name="plan_coverage")
public class PlanCoverage {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private long coverage_id;
#Column(name="plan_code")
private String plan_code;
#Column(name="coverage_description")
private String coverage_description;
/..getters and setters
#ManyToOne()
#JoinColumn(name="plan_code", referencedColumnName = "plan_code",insertable=false, updatable=false)
private Plan plan;
public Plan getPlan() {
return plan;
}
public void setPlan(Plan plan) {
this.plan = plan;
}
}
Please help me. I've been stuck with these for a few days and non of the tutorials seems to work on me. Thank you so much!!
You have messed up with the convention that spring boot is using to compose query methods. The case of the fields in the entity should follow the lower camel-case scheme, like so:
#Column(name="plan_code")
private String planCode;
and then the query method in PlanCoverageRepository should be:
List<PlanCoverage> findAllByPlanCode(String planCode);
I have a spring boot (1.5.4.RELEASE) project using Java 8. I have an entity and it's related domain class like this:
#Entity
#Table(name = "Foo", schema = "dbo")
public class FooEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private int id;
#Column(name="Name")
private String name;
#Column(name="Type")
private String type;
#Column(name="Color")
private String color;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "Car")
private Car car;
//getter and setter
}
public class Foo {
private int id;
private String name;
private String type;
private String color;
private Car car;
//Constructors and getters
}
I want to create a repository that fetches this Foo object from the DB but only fetching the complex fields if the user asks for them to prevent unnecessary join statements. The repo looks like this:
import static com.test.entities.QFooEntity.fooEntity;
import static com.test.entities.QCarEntity.carEntity;
#Repository
public class FooRepository {
private final JPAQuery<FooEntity> query = createQuery().from(fooEntity);
public FooRepository getFooByName(String name) {
query.where(fooEntity.name.eq(name));
return this;
}
public FooRepository withCar() {
query.leftJoin(fooEntity.car, carEntity).fetchJoin();
return this;
}
public Foo fetch() {
FooEntity entity = query.fetchOne();
return FooMapper.mapEntityToDomain().apply(entity);
}
}
So a barebones call for a Foo object will return the Entity with values for all the fields except for the car field. If the user wants car information then they have to explicitly call withCar.
Here is the mapper:
public class FooMapper {
public static Function<FooEntity, Foo> mapEntityToDomain() {
return entity -> {
return new Foo(e.getId(), e.getName(), e.getType(), e.getColor(), e.getCar());
};
}
}
The problem is when you do e.getCar() if the value is not there (i.e. there's a proxy present) JPA will go out and fetch it for you. I don't want this to be the case. It will just grab the values and map them to the domain equivalent if it's not there then null.
One solution that I've heard (and tried) is calling em.detach(entity); however, this doesn't work as I intended because it throws an exception when you try to access getCar and I've also heard this is not best practice.
So my question is what is the best way to create a repo using a builder pattern on a JPA entity and not have it call the DB when trying to map.
You could create a utility method that will return null if the given object is a proxy and is not initialized:
public static <T> T nullIfNotInitialized(T entity) {
return Hibernate.isInitialized(entity) ? entity : null;
}
Then you can call the method wherever you need it:
return new Foo(e.getId(), e.getName(), e.getType(), e.getColor(), nullIfNotInitialized(e.getCar()));
Just map it to a new object and leave out the Car relation, this is the standard approach. You can use MapStruct and just ignore the car field during mapping: http://mapstruct.org/documentation/stable/reference/html/#inverse-mappings
Just don't map the car... Map a field holding the ID and use another method to get the actual Car. I would use a distinctive method name, to differentiate it from the other getters.
class FooEntity {
#Column
private int carId;
public int getCarId() {
return carId;
}
public void setCarId(int id) {
this.carId = id;
}
public Car fetchCar(CarRepository repo) {
return repo.findById(carId);
}
}
You can write query on top of JPA
#Query("select u from Car c")
import org.springframework.data.repository.CrudRepository;
import com.example.model.FluentEntity;
public interface DatabaseEntityRepository extends CrudRepository<FooEntity , int > {
}
As you said
I don't want this to be the case. It will just grab the values and map them to the domain equivalent, if it's not there then null.
Then you just set it to null, because the field car will always not be there.
Otherwise, if you mean not there is that the car not exists in db, for sure a subquery(call the proxy) should be made.
If you want to grab the car when call Foo.getCar().
class Car {
}
class FooEntity {
private Car car;//when call getCar() it will call the proxy.
public Car getCar() {
return car;
}
}
class Foo {
private java.util.function.Supplier<Car> carSupplier;
public void setCar(java.util.function.Supplier<Car> carSupplier) {
this.carSupplier = carSupplier;
}
public Car getCar() {
return carSupplier.get();
}
}
class FooMapper {
public static Function<FooEntity, Foo> mapEntityToDomain() {
return (FooEntity e) -> {
Foo foo = new Foo();
foo.setCar(e::getCar);
return foo;
};
}
}
Make sure you have the db session ,when you call Foo.getCar()
You could try adding state to your repository and influence the mapper. Something like this:
import static com.test.entities.QFooEntity.fooEntity;
import static com.test.entities.QCarEntity.carEntity;
#Repository
public class FooRepository {
private final JPAQuery<FooEntity> query = createQuery().from(fooEntity);
private boolean withCar = false;
public FooRepository getFooByName(String name) {
query.where(fooEntity.name.eq(name));
return this;
}
public FooRepository withCar() {
query.leftJoin(fooEntity.car, carEntity).fetchJoin();
withCar = true;
return this;
}
public Foo fetch() {
FooEntity entity = query.fetchOne();
return FooMapper.mapEntityToDomain(withCar).apply(entity);
}
}
In your mapper, you then include a switch to enable or disable car lookups:
public class FooMapper {
public static Function<FooEntity, Foo> mapEntityToDomain(boolean withCar) {
return e -> {
return new Foo(e.getId(), e.getName(), e.getType(), e.getColor(), withCar ? e.getCar() : null);
};
}
}
If you then use new FooRepository().getFooByName("example").fetch() without the withCar() call, e.getCar() should not be evaluated inside FooMapper
You may want to use the PersistentUnitUtil class to query if an attribute of entity object is already loaded or not. Based on that you may skip the call to corresponding getter as shown below. JpaContext you need to supply to user entity bean mapper.
public class FooMapper {
public Function<FooEntity, Foo> mapEntityToDomain(JpaContext context) {
PersistenceUnitUtil putil = obtainPersistentUtilFor(context, FooEntity.class);
return e -> {
return new Foo(
e.getId(),
e.getName(),
e.getType(),
e.getColor(),
putil.isLoaded(e, "car") ? e.getCar() : null);
};
}
private PersistenceUnitUtil obtainPersistentUtilFor(JpaContext context, Class<?> entity) {
return context.getEntityManagerByManagedType(entity)
.getEntityManagerFactory()
.getPersistenceUnitUtil();
}
}
I have a CrudRepository that is supposed to make a query with an array (findByIn). In my repository tests it works, but when I try to use the query in my service, it doesn't work. Could someone explain why it doesn't work? Here is my setup (excluding some code irrelevant to the question)
Database model:
#Entity
#Table(name="Place")
public class Place implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "placeId", nullable = false)
private Long placeId;
#Column(name = "owner", nullable = false)
private String owner;
public Long getPlaceId() {
return placeId;
}
public void setPlaceId(Long placeId) {
this.placeId = placeId;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
}
Repository:
#Repository
public interface PlaceRepository extends CrudRepository<Place, Long> {
List<Place> findByPlaceIdIn(Long[] placeId);
}
Service (this is the part not working):
#Service
public class PlaceService {
#Autowired
private PlaceRepository placeRepository;
public List<Place> getPlaces(Long[] placeIds) {
return placeRepository.findByPlaceIdIn(placeIds);
}
}
The problem is that in my service placeRepository.findByPlaceIdIn(placeIds) returns 0 objects if placeIds contains more than one item. If placeIds contains just one item, the query works fine. I tried replacing return placeRepository.findByPlaceIdIn(placeIds) with this piece of code that does the query for every array item one by one (this actually works, but I'd like to get the query work as it should):
ArrayList<Place> places = new ArrayList<Place>();
for (Long placeId : placeIds) {
Long[] id = {placeId};
places.addAll(placeRepository.findByPlaceIdIn(id));
}
return places;
I know that the repository should work, because I have a working test for it:
public class PlaceRepositoryTest {
#Autowired
private PlaceRepository repository;
private static Place place;
private static Place place2;
private static Place otherUsersPlace;
#Test
public void testPlacesfindByPlaceIdIn() {
place = new Place();
place.setOwner(USER_ID);
place2 = new Place();
place2.setOwner(USER_ID);
place = repository.save(place);
place2 = repository.save(place2);
Long[] ids = {place.getPlaceId(), place2.getPlaceId()};
assertEquals(repository.findByPlaceIdIn(ids).size(), 2);
}
}
I also have another repository for other model, which also uses findByIn and it works fine. I can't see any relevant difference between the repositories. I thought it might offer some more details to show the working repository, so I included it below:
Database model:
#Entity
#Table(name="LocalDatabaseRow")
#JsonIgnoreProperties(ignoreUnknown=false)
public class LocalDatabaseRow implements Serializable {
public LocalDatabaseRow() {}
public LocalDatabaseRow(RowType rowType) {
this.rowType = rowType;
}
public enum RowType {
TYPE1,
TYPE2
};
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
#JsonProperty("id")
private Long id;
#JsonProperty("rowType")
#Column(name = "rowType")
private RowType rowType;
public Long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public RowType getRowType() {
return rowType;
}
public void setRowType(RowType rowType) {
this.rowType = rowType;
}
}
Repository:
#Repository
public interface LocalDatabaseRowRepository extends CrudRepository<LocalDatabaseRow, Long> {
List<LocalDatabaseRow> findByRowTypeAndUserIdIn(RowType type, String[] userId);
}
try using a list instead :
findByPlaceIdIn(List placeIdList);
You have a typo in your code (the repository declaration in the service):
#Autowired
private placeRepository placeRepository;
Should be:
#Autowired
private PlaceRepository placeRepository;