Home > Software design >  I want to save two entities in one transaction with JPA if any error occure while inserting the data
I want to save two entities in one transaction with JPA if any error occure while inserting the data

Time:03-13

for example i have two entities: first is student and another is address. i want to insert data on both tables with foreign key relationship, but their is one condition: if any error occurs while inserting the data in any tables the all changes should be roll back.

how can i do please help

class Student{
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private int id;

   private String firstName;

   @OneToOne(fetch = FetchType.EAGER)
   @JoinColumn(name="address_id",referencedColumnName = "id")
   private Address address;
}

class Address {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private int id;

   private String city;

}

CodePudding user response:

This is a very general question, so I will try to point you in the right direction. For this I assume:

  • you are using spring-data-jpa
  • you have a datasource already configured
  • your application starts up correctly
  • you already configured the @Repository or you know how to use the EntityManager via @PersistenceContext
  • I will assume the Repository solution

The good news is: Persisting objects that are marked using OneToOne, OneToMany, ManyToOne and ManyToMany is a core feature, so most of this is already handled by JPA/Hibernate. All you have to ensure is that you read the documentation and follow it.

First step is to understand Cascading. Cascading is not turned on by default. There are tons of resources on the web, that help you understand this, I will point you to this one. This means that you will need to set the cascade attribute on the OneToOne annotation (see referenced tutorial).

Once you have done this, you should be ready to roll, given the above assumptions hold. You can now build a student, e.g.

@DataJpaTest
class StudentRepositoryTest {

@Autowired
private StudentRepository studentRepository;

@Autowired
private AddressRepository addressRepository;

@BeforeEach
void setup() {
    assumeThat(studentRepository).isNotNull();
    assumeThat(addressRepository).isNotNull();
}

@Test
void testSaveStudent {
   assumeThat(studentRepository.count()).isZero();
   assumeThat(addressRepository.count()).isZero();
   var student = new Student();
   var address = new Address();
   address.setCity("New York");
   student.setFirstName("Max");
   student.setAddress(address);
   studentRepository.save(student);
   assertThat(studentRepository.count()).isOne();
   assertThat(addressRepository.count()).isOne();
}

Second: In regards to your transaction question. Per default when you invoke a repository's save method a transaction is started. So in the very base case you don't necessarily need a transaction. Although, once you deal with more complex business cases it's necessary to understand JPA's transaction manager. Transactions usually happen on the service layer. See links below for more info on Transactions.

@Service
@Transactional(readOnly = true)
public class StudentService {
   @Transactional
   public Student assignStudentToAddress(Student s, Address a) {
      s.setAddress(a);
      return studentRepository.save(s);
   }
}

Some thoughts:

  • Are you sure that one student can only stay at one address? This would mean that living arrangements like in Big Bang Theory (shared apartment) wouldn't work. For testing purposes this works of course.
  • Consider the reverse: what if we have an address screen and we want to add students to it?

More resources:

CodePudding user response:

With Stateless EJB :

@Stateless
public StudentDAO {
    @PersistenceContext
    private EntityManager em;

    public Student createAddressAndStudent(Student student, Address address) {
        em.persist(address);
        student.setAdress(address);
        em.persist(student);
        return student;
    }
}
  • Related