1

I have two entities. Person (one) and Contactdetails (many). Contactdetails are lazy loading. I have no problem with creating a new Person and get it written into the database. For my test I use the MySQL database installed locally filled up with dummy data. I run kind of integration test.

In my test code I use @Transactional annotation in order to stay inside one session where I fetch a Person, create a new Contacdetails, connect them together and then save the Person, which will cascade save the Contactdetails too. In theory...

Contactdetails is not written to the database. Interestingly, if I write to console all Contactdetails inside the @Transactional annotated test method, I see the new Contactdetails created. As soon es I leave this test method, this freshly created Contactdetails is no more visible.

My Entities are as follows:

Person:

package com.szivalaszlo.contracts.landon.data.entity;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
import java.time.LocalDate;
import java.util.Objects;

@Entity
@Table(name="person")
public class Person {

    private static Logger logger = LogManager.getLogger();

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    @Column(name = "first_name")
    private String firstName;
    @Column(name = "last_name")
    private String lastName;
    @Column(name = "date_of_birth")
    private LocalDate dateOfBirth;
    @Column(name = "first_name_mother")
    private String firstNameMother;
    @Column(name = "last_name_mother")
    private String lastNameMother;

    @OneToMany(fetch=FetchType.LAZY, mappedBy = "person", cascade = CascadeType.ALL) // refers to person attribute of Contactdetails class
    private List<Contactdetails> contactdetails;

    @ManyToMany(fetch=FetchType.LAZY)
            @JoinTable(name = "buyer_contract",
            joinColumns = @JoinColumn(name = "person_buyerid"),
            inverseJoinColumns = @JoinColumn(name = "contractid"))
    List<Contract> buyerContracts;

    @ManyToMany(fetch=FetchType.LAZY)
    @JoinTable(name = "seller_contract",
            joinColumns = @JoinColumn(name = "person_sellerid"),
            inverseJoinColumns = @JoinColumn(name = "contractid"))
    List<Contract> sellerContracts;

    public Person(){

    }

    public Person(String firstName, String lastName, String dateOfBirth, String firstNameMother, String lastNameMother) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.dateOfBirth = LocalDate.parse(dateOfBirth);
        this.firstNameMother = firstNameMother;
        this.lastNameMother = lastNameMother;
    }


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public LocalDate getDateOfBirth() {
        return dateOfBirth;
    }

    public void setDateOfBirth(LocalDate dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
    }

    public String getFirstNameMother() {
        return firstNameMother;
    }

    public void setFirstNameMother(String firstNameMother) {
        this.firstNameMother = firstNameMother;
    }

    public String getLastNameMother() {
        return lastNameMother;
    }

    public void setLastNameMother(String lastNameMother) {
        this.lastNameMother = lastNameMother;
    }

    public List<Contactdetails> getContactdetails() {
        return contactdetails;
    }

    public void addContactdetail(Contactdetails contactdetail){
        if(null == contactdetails){
            contactdetails = new ArrayList<Contactdetails>();
        }
        contactdetails.add(contactdetail);
    }

    public String getStringForEqualsCheck(){
        return firstName+lastName+dateOfBirth.toString()+firstNameMother+lastNameMother;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) return false;
        if (!(obj instanceof Person))
            return false;
        if (obj == this)
            return true;
        return this.getStringForEqualsCheck().equals(((Person) obj).getStringForEqualsCheck());
    }

    @Override
    public int hashCode() {
        return Objects.hash(firstName, lastName, dateOfBirth, firstNameMother, lastNameMother);
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", dateOfBirth=" + dateOfBirth +
                ", firstNameMother='" + firstNameMother + '\'' +
                ", lastNameMother='" + lastNameMother + '\'' +
                '}';
    }
}

Contactdetails:

package com.szivalaszlo.contracts.landon.data.entity;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.persistence.*;
import java.util.Objects;

@Entity
@Table(name="contactdetails")
public class Contactdetails {

    private static Logger logger = LogManager.getLogger();

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    @Column(name = "address")
    private String address;
    @Column(name = "email")
    private String email;
    @Column(name = "phone")
    private String phone;

    @ManyToOne
    @JoinColumn(name = "personid", nullable = false)
    private Person person;

    public Contactdetails(){

    }

    public Contactdetails(String address, String email, String phone) {
        this.address = address;
        this.email = email;
        this.phone = phone;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
        logger.debug("Person is set for contactdetail: " + this.toString() + " person: " + this.person.toString());
    }

    public String getStringForEqualsCheck(){
        return address+email+phone;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) return false;
        if (!(obj instanceof Contactdetails))
            return false;
        if (obj == this)
            return true;
        return this.getStringForEqualsCheck().equals(((Contactdetails) obj).getStringForEqualsCheck());
    }

    @Override
    public int hashCode() {
        return Objects.hash(address, email, phone);
    }

    @Override
    public String toString() {
        return "Contactdetails{" +
                "id=" + id +
                ", address='" + address + '\'' +
                ", email='" + email + '\'' +
                ", phone='" + phone + '\'' +
                ", person=" + person +
                '}';
    }
}

Service class:

package com.szivalaszlo.contracts.landon.business.domain;

import com.szivalaszlo.contracts.landon.data.entity.Contactdetails;
import com.szivalaszlo.contracts.landon.data.entity.Person;
import com.szivalaszlo.contracts.landon.data.repository.ContactdetailsRepository;
import com.szivalaszlo.contracts.landon.data.repository.PersonRepository;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashSet;
import java.util.List;


@Service
public class PersonService {

    private static Logger logger = LogManager.getLogger();

    private PersonRepository personRepository;
    private ContactdetailsRepository contactdetailsRepository;

    @Autowired
    public PersonService(PersonRepository personRepository, ContactdetailsRepository contactdetailsRepository){
        this.personRepository = personRepository;
        this.contactdetailsRepository = contactdetailsRepository;
    }

    public int createPerson(String firstName, String lastName, String dateOfBirth, String firstNameMother, String lastNameMother){
        Person person = new Person(firstName, lastName, dateOfBirth, firstNameMother, lastNameMother);
        if(personAlreadyExistsInDb(person)){
            logger.debug("Same person already found in Db. Person: " + person.toString());
            return -1;
        }else{
            personRepository.save(person);
            return person.getId();
        }
    }

    private boolean personAlreadyExistsInDb(Person person){
        HashSet<Person> allPersonFromDb = personRepository.findAll();
        if (allPersonFromDb.contains(person)){
            return true;
        }else{
            return false;
        }
    }

    public void createContactdetailsForPerson(Person person, String address, String email, String phone){
        Contactdetails contactdetails = new Contactdetails(address, email, phone);

        if(contactdetailsAlreadyExistForPerson(contactdetails, person)){
            logger.debug("Same contactdetail for person already found " + person.toString() + " " + contactdetails.toString());
        } else{
            contactdetails.setPerson(person);
            person.addContactdetail(contactdetails);
            contactdetailsRepository.save(contactdetails);
            personRepository.save(person);
        }
    }

    private boolean contactdetailsAlreadyExistForPerson(Contactdetails contactdetails, Person person){
        List<Contactdetails> allContactdetailsForPersonFromDb = person.getContactdetails();
        if(null == allContactdetailsForPersonFromDb || allContactdetailsForPersonFromDb.size() == 0){
            return false;
        }
        if(!allContactdetailsForPersonFromDb.contains(contactdetails)){
            return false;
        }
        return true;
    }
}

Test class:

package com.szivalaszlo.contracts;

import com.szivalaszlo.contracts.landon.business.domain.PersonService;
import com.szivalaszlo.contracts.landon.data.entity.Contactdetails;
import com.szivalaszlo.contracts.landon.data.entity.Person;
import com.szivalaszlo.contracts.landon.data.repository.ContactdetailsRepository;
import com.szivalaszlo.contracts.landon.data.repository.PersonRepository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Random;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)

public class PersonServiceTest_ {

    private static Logger logger = LogManager.getLogger();

    @Autowired
    private PersonRepository personRepository;

    @Autowired
    private ContactdetailsRepository contactdetailsRepository;

    Random rand = new Random();
    int randomNumber = rand.nextInt(899)+100;

    private String address = "test address street 1 in City "+randomNumber;
    private String email = "testemail@exmaple.com " +randomNumber;
    private String phone = "+41 12 345 78 90 " +randomNumber;

    @Test
    @Transactional(propagation = Propagation.REQUIRED)
    public void it_should_save_contactdetail_to_person(){
        PersonService testPersonService = new PersonService(personRepository, contactdetailsRepository);

        Person testPerson = personRepository.findById(179); //I have an id# 179 Person in the database fully detailed.

        testPersonService.createContactdetailsForPerson(testPerson, address, email, phone);

        List<Contactdetails> allStoredContactdetailsinDB = contactdetailsRepository.findAll();

        allStoredContactdetailsinDB.forEach(item->System.out.println(item));
    }
}

The test runs with no error. I see the following output in the console:

Bradlystad, ND 98886-5789', email='maverick85@example.com', phone='464-812-3618', person=Person{id=98, firstName='Eldridge', lastName='Reichel', dateOfBirth=1981-08-07, firstNameMother='Brianne', lastNameMother='Ryan'}}
Contactdetails{id=99, address='569 Langosh Turnpike Suite 235
East Archibald, FL 43208-3081', email='spouros@example.org', phone='08976297815', person=Person{id=99, firstName='Myrtie', lastName='Graham', dateOfBirth=1982-01-19, firstNameMother='Libby', lastNameMother='Veum'}}
Contactdetails{id=100, address='010 Pfeffer Islands
Kiehnside, FL 25044-1157', email='paucek.grover@example.com', phone='1-157-850-0688x390', person=Person{id=100, firstName='Katheryn', lastName='Hoppe', dateOfBirth=2009-06-22, firstNameMother='Virginie', lastNameMother='Donnelly'}}

Contactdetails{id=106, address='test address street 1 in City 623', email='testemail@exmaple.com 623', phone='+41 12 345 78 90 623', person=Person{id=179, firstName='TestJhonFirst808', lastName='TestSmithLast808', dateOfBirth=1990-11-11, firstNameMother='TestJackieMotherFirst808', lastNameMother='TestSmithMotherLast808'}}

The interesting part is the last line which shows, that the Contactdetails is created in the db with id#106.

When I query the database after the test is run, I don't see the new line in the table.

Lac
  • 71
  • 1
  • 5

1 Answers1

1

By default, test transactions will be automatically rolled back after completion of the test; however, transactional commit and rollback behavior can be configured declaratively via the @Commit and @Rollback annotations

Add the following annotation to your test:

@Rollback(false)
Selindek
  • 3,269
  • 1
  • 18
  • 25
  • Thanks @Selindek! It worked. By the way I write Person to the database inside one '@Test' also, but they are written with no problem. It seems, that '@Transactional' with inside and '@Test' rolles is back and not the '@Test' itself. Right? – Lac Feb 24 '19 at 06:02
  • Yep. The text from the documentation says exactly that. – Selindek Feb 24 '19 at 08:40