이제 Student 라는 이름의 DAO 를 하나 생성해보자. 순서는 다음과 같다.

  1. DAO Interface 를 define
  2. DAO Implement 을 define (+inject entity manager)
  3. main app 을 update
Step 1: Define DAO Interface
import com.lucvs.cruddemo.entity.Student;
 
public interface StudentDAO {
	void save(Student theStudent);
}

위와 같이 Interface 형태로 StudentDAO 를 define 하고, save method 를 declare 한다. save method 는 Implementation 에서 overriding 을 통해 implement 하여 사용하면 될 것이다.

Step 2: Define DAO Implementation
public class StudentDAOImpl implements StudentDAO {
 
	private EntityManager entityManager;
 
	@Autowired
	public StudentDAOImpl(EntityManager theEntityManager) {
		entityManager = theEntityManager;
	}
 
	@Override
	@Transactional
	public void save(Student theStudent) {
		entityManager.persist(theStudent);	
	}
}

Constructor 부분에서, JPA Entity Manager 에서 언급한대로 Spring Boot 가 Entity Manager 를 자동으로 생성해주기 떄문에 @Autowired 를 통하여 EntityManager 를 injection 해주면 된다.

그리고 StudentDAO Interface 에서 declare 한 save method 를 overriding 하여 implement 한다. inject 받은 EntityMangerpersist method 를 통하여 Student object 를 database 에 저장한다.

@Transactional annotation 은 method 나 class 에 Transaction 을 적용할 수 있도록 해준다.

Transaction

Transaction 은 Database 작업을 하나의 logic 단위로 묶어, 모든 작업이 성공적으로 완료되거나 실패 시 모두 취소되도록 보장하는 기술이다.

Specialized Annotation for DAOs

Spring 은 @Repository 라는 annotation 을 제공한다. @Repository annotation 은 @Component 의 특수화된 형태로, component-scanning 을 통하여 DAO Implementation 을 Bean 의 형태로 자동 등록한다.

또한Database 작업 중 발생하는 다양한 기술 종속적인 JDBC Exception(ex. SQLExceptionHibernateException 등)들을 Spring이 제공하는 DataAccessException 계열의 Exception 으로 변환해준다.

@Repository
public class StudentDAOImpl implements StudentDAO {
	...
}

위와 같이 DAO Implementation class 에 대하여 @Repository annotation 을 사용해주면 된다.

Step 3. Update Main Application
@SpringBootApplication
public class CruddemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(CruddemoApplication.class, args);	
	}
 
	@Bean
	public CommandLineRunner commandLineRunner(StudentDAO studentDAO) {
		return runner -> {
			createStudent(studentDAO);
		}
	}
 
	private void createStudent(StudentDAO studentDAO) {
		// create the student object
		...
		// save the student object
		...
		// display id of the saved student
		...
	}
}

그리고 마지막으로, StudentDAO 를 Main Application 에 Inject 하여 Database 와 Interacting 할 수 있다.

Student DAO - Test (singular saving)
private void createStudent(StudentDAO studentDAO) {  
  
    // create the student object  
    System.out.println("Creating new student object ...");  
    Student tempStudent = new Student("Arne", "Slot", "arne@liverpool.com");  
  
    // save the student object  
    System.out.println("Saving the student ...");  
    studentDAO.save(tempStudent);  
  
    // display id of the saved student  
    System.out.println("Saved a student. Generated id: " + tempStudent.getId());  
}

createStudent method 도 위와 같이 작성한 이후에 Application 을 실행하여 tempStudent object 에 대한 data 가 Database 에 잘 저장되었을지 테스트해보자.

Application 을 실행하면 console 화면에 위와 같이 code 의 실행 상에서는 문제가 없는 것으로 나왔다. 그럼 실제 Database 에 theStudent obejct 가 잘 저장되었는지 확인해보자.

SELECT * FROM student_tracker.student;

위와 같이 SELECT 를 이용하여 query 를 날려주면 이전에 만들어놓은 student table 을 조회할 수 있다.

성공적으로 Database 에 직접 생성한 theStudent object 의 data 가 저장된 것을 볼 수 있다.

결과에서 볼 수 있듯이, theStudent object 를 만들 때 Contructor 의 argument 에 id 값을 넣어서 initialize 해주지 않았음에도 불구하고 1 이라는 값이 들어가 있다.

이로써 이전에 Student Entity Class 를 만들 때 id field 에 대하여 지정한 ID Generation Strategy 역시도 정상적으로 동작한 것을 알 수 있다.

Student DAO - Test (multiple saving)

이번에는 여러 개의 Student object 를 생성하여 Database 에 저장한 이후, PK(Primary Key) 로 설정한 id column 에 값들이 잘 저장되는지 확인해보자.

id에 대하여GenerationType.IDENTITYstrategy 를 사용했기 때문에, DB 내부에서는AUTO_INCREMENT` 로 동작한다.

추가적으로, MySQL Workbench 에서 특정 table 을 우클릭하면 위와 같은 메뉴가 나오는데, 여기서 Alter Table

Changing Index of MySQL Auto Increment

기본적으로 Auto-Increment 를 사용하게 되면 index 가 1 부터 시작되는 것을 확인할 수 있었다. 그러나 다음 SQL query 를 통하여 시작 index 를 원하는 숫자로 바꿀 수 있다.

ALTER TABLE student_tracker.student AUTO_INCREMENT=1000

이후에 Application 을 실행하면 위와 같이 PK 의 시작 index 가 ALTER query 를 통하여 설정한 1000 부터 시작하는 것을 알 수 있다.

TRUNCATE student_tracker.student

참고로 위와 같이 TRUNCATE query 를 실행하면 해당 table 의 모든 row 를 삭제한다. primary key 역시 시작 index 로 다시 초기화된다.