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 theEntityManager
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:
- Beginner's guide to mapping by VladMihalcea
- Mapping
OneToOne
beginner friendly: by Baeldung and slightly more advanced by VladMihalcea - Mapping
OneToMany
beginner friendly: by baeldung and slightly more advanced: by VladMihalcea - Using
Transactional
by VladMihalcea and by MarcoBehler
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;
}
}