레이블이 JPA인 게시물을 표시합니다. 모든 게시물 표시
레이블이 JPA인 게시물을 표시합니다. 모든 게시물 표시

Hibernate LazyInitializationException 해결

LazyInitializationException


Hibernate를 이용하여 개발하다보면, 자주 늘 만나는 녀석이다.
이러한 현상이 발생하는 이유는 다음과 같다.


1. 조회 서비스가 Select를 위한 서비스 (트랜잭션이 걸린)에 조회 요청을 한다.
2. 조회 결과가 반환 되면서 트랜잭션 종료
    - 이때 Entity는 영속상태가 아니라, 준영속 상태로 빠진다.
    - 만약 EAGER 패치로 가져왔다면 Entity의 하위 Entity가 함께 가져왔을 것이다.
      그러나 Lazy로 가져온 경우라면 하위 엔터티는 존재하지 않는 상태임.
3. Lazy 로 가져온 데이터가 준 영속 상태에서 하위 엔터티를 조회하면 LazyInitializationException이 발생한다.


해결방법 :

1. Use Hibernate.initialize :
   Hibernate.initialize 를 이용하여 하위 엔터티를 초기화 한다.
   (여기서 초기화라는 것은 프록시에 해당 엔터티 객체들을 로드해 둔다는 의미이다.)

Hibernate.initialize(topics.getComents());

2. Use Join FETCH
    - JPQL에서 JOIN FETCH문법을 이용하여 한번에 조인 결과를 다 가져온다.
    - EAGER 패치 설정을 기본으로 걸어서 한번에 결과를 가져온다.

3. Use OpenSessionInViewFilter / OpenSessionInViewInteceptor
    - LazyInitializationException은 View에서 보통 많이 발생한다. 이러한 경우 SessionInViewFilter를 걸어서 필터단까지 영속성을 가지고 가거나, 더 짧게는 SessionInViewInterceptor를 걸어 핸들러 단까지만 영속성을 가지고 가는 방법이 있다.

    - 사용시 다음을 주의해야한다.
        - 의도하지 않은 DB변경이 발생하지 않도록 주의해야한다.
        - 성능에 문제를 일으킬 수 있다.

<filter>
    <filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

# 참고 : 
- 일반적으로 LazyInitializationException은 View를 그릴때 해당 문제가 발생한다.
- 만약 데이터 저장시 발생하는 문제라면, 혹은 서비스단에서 해당 데이터 조합을 미리 가져와서 뷰에대한 데이터를 만들어 줄 수 있다면, 트랜잭션을 Service단까지 올려서 처리하면 된다.

Simple JPA Test

Simple JPA Test

Simple JPA Test

단순한 JPA의 OneToMany, ManyToOne 관계에 대한 테스트를 해보자.

1. JPA테스트 :

- JPA에서 OneToMany와 ManyToOne을 양방향 연결 관계로 설정하고, 이를 운용하는 테스트를 진행해보았다.
- 여기서는 CascadeType.ALL의 설정과 orphanRemove = true에 대해서 알아보고자 한다. 
- 또한 Unique 설정에서 해당 처리가 어떻게 수행되는지 알아 볼 것이다. 

2. Teacher Entity 객체 생성하기.

package kr.co.unclebae.jpa.domain;

import com.google.common.collect.Lists;
import org.springframework.util.CollectionUtils;

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

/** * Created by UncleBae on 15. 9. 14.. */@Entity@Table(name = "TEACHER")
public class Teacher {
    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "TEACHER_ID")
    private Long id;

    private String name;

    private String subject;

    @OneToMany(mappedBy = "teacher" ,fetch = FetchType.LAZY
cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Student> students = Lists.newArrayList();

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        if (students == null) {
            return;
        }

        for(Student student : students) {
            addStudent(student);
        }
    }

    public void addStudent(Student student) {
        if (students.contains(student)) {
            return;
        }
        this.students.add(student);
        student.setTeacher(this);
    }

    public void removeStudent(Student student) {
        if (student == null) {
            return;
        }

        students.remove(student);
        student.setTeacher(null);
    }

    public void removeAllStudents() {
        if (CollectionUtils.isEmpty(students)) {
            return;
        }

        for(Student student : students) {
            removeStudent(student);
        }
    }

    @Override    public String toString() {
        return String.format("Teacher ID %s, name %s, 
subject %s, students %s", id, name, subject
createStudentsString());
    }

    private String createStudentsString() {
        StringBuffer sb = new StringBuffer();
        sb.append("[");
        if (!CollectionUtils.isEmpty(students)) {
            for (Student student : students) {
                sb.append("{");
                sb.append(student.toString());
                sb.append("},");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    @Override    public boolean equals(Object obj) {
        return super.equals(obj);
    }
}


- Teacher : Student = 1 : N관계 즉 OneToMany관계이다.

- 여기서 mappedBy를 이용하여 연관관계의 주인이 아님을 설정한다. (연관관계의 주인은 보통 Many쪽에 지정한다. )

- cascade = CascadeType.ALL 을 지정하였다.
    이로 인해 Teacher의 영속성 변화에 대해서 자식인 Student에게 전이를 시켜준다. (즉, 부모가 영속화 되면 자식도 영속화 되며, 부모에서 제거되면 자식도 제거되는 처리를 수행한다.)

- orphanRemove = true 를 지정하였다.
    이렇게 지정하면 Teacher객체를 제거하면, 자식인 Student의 객체들도 함께 제거된다.
    또한 Student에 대해서 student.setTeacher(null); 이라고 지정하면 해당 자식은 제거 된다.

- addStudent, removeStudent, removeAllStudents와 같은 편의 메소드를 부모쪽에 지정하여 자식 객체에 대한 연관관계를 함께 추가 및 제거해주도록 처리한다.

3. Student Entity 객체 생성하기.

package kr.co.unclebae.jpa.domain;

import javax.persistence.*;

/** * Created by UncleBae on 15. 9. 14..
 */@Entity@Table(name = "STUDENT")
public class Student {

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

    @Column(unique = true)
    private String name;

    private String grade;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TEACHER_ID")
    private Teacher teacher;

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public String getGrade() {
        return grade;
    }

    public void setGrade(String grade) {
        this.grade = grade;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;

        if (teacher == null) {
            return;
        }

        if ( !teacher.getStudents().contains(this)) {
            teacher.getStudents().add(this);
        }
    }

    @Override    public String toString() {
        return String.format("Student id %s, name %s, 
grade %s", id, name, grade );
    }
}

- ManyToOne 를 지정하여 자식 객체에서 부모와의 연관을 맺어준다.

- ManyToOne에서는 cascade를 사용하지 말것. - 만약 cascade = CascadeType.ALL을 지정했을때 테스트를 해보면 자식객체에서 부모 객체를 제거하면, Teacher객체 역시 함께 제거 되어 버린다. (주의)

4. 리포지토리 생성하기 

TeacherRepository.java

package kr.co.unclebae.jpa.repository;

import kr.co.unclebae.jpa.domain.Teacher;
import org.springframework.data.jpa.repository.JpaRepository;

/** * Created by Uncle Bae on 15. 9. 14.. */
public interface TeacherRepository 
    extends JpaRepository<Teacher, Long> {
}
StudentRepository.java
package kr.co.unclebae.jpa.repository;

import kr.co.unclebae.jpa.domain.Student;
import org.springframework.data.jpa.repository.JpaRepository;

/** * Created by naver on 15. 9. 14.. */
public interface StudentRepository 
    extends JpaRepository<Student, Long> {
}


- SpringData JPA를 이용하였다. 

- JpaRepository<T, ID>를 상속받은 인터페이스를 생성하면, 실행시점에 해당 구현체를 만들어준다. 

5. 테스트 케이스 작성하기.

package kr.co.unclebae.jpa.service;

import com.google.common.collect.Lists;
import kr.co.unclebae.jpa.domain.Student;
import kr.co.unclebae.jpa.domain.Teacher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import java.util.List;

/** * Created by Uncle Bae on 15. 9. 14.. */
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration@ContextConfiguration
{"classpath:hibernateConfig.xml"})
public class TeacherTest {

    @Autowired    
    private TeacherService teacherService;

    @Test    
    public void TeacherInsertTest() {
        Teacher teacher = createTeacherFixture();

        Teacher savedTeacher = teacherService.save(teacher);

        System.out.println(String.format("Saved Val : %s", savedTeacher));
    }

    @Test    public void TeacherInsertAndSutdentUpdate() {

        // 일반적인 CascadeType.ALL 테스트        
        Teacher teacherFixture = createTeacherFixture();

        Student student1 = createStudentFixture("STUDENT1", "B-");
        Student student2 = createStudentFixture("STUDENT2", "A+");

        teacherFixture.addStudent(student1);
        teacherFixture.addStudent(student2);

        Teacher teacherSaved = teacherService.save(teacherFixture);
        System.out.println( String.format("[GENERAL CascadeType.ALL] 
SAVED TEACHER %s", teacherSaved.toString() ) );

        // Delete And Insert And Update        
        List<Student> students = Lists.newArrayList();
        Student student3 = createStudentFixture("STUDENT3", "A-");
        students.add(student3);

        Student student4 = createStudentFixture("STUDENT1", "F");
        student4.setId(student1.getId());  
        // Update가 되려면 ID를 동일하게 세팅해야한다.        
        students.add(student4);

        teacherService.deleteAndMerge(teacherSaved.getId(), students);
        Teacher teacherSelected = teacherService.findById(teacherSaved.getId());

        System.out.println( String.format("[CascadeType.REMOVE 
or CascadeType.ALL] Delete And Merge Result %s"
teacherSelected ) );

        teacherService.removeFirstStudent(teacherSelected.getId());
        Teacher teacherSelected2 = teacherService.findById(teacherSaved.getId());

        System.out.println(String.format("[orphanRemove = true] 
RemoveFirstStudent Result %s", teacherSelected2));

        teacherService.deleteById(teacherSelected.getId());
        System.out.println( String.format("[orphanRemove = true] 
delete parent") );
    }

    private Student createStudentFixture(String name, String grade) {
        Student student = new Student();
        student.setName(name);
        student.setGrade(grade);
        return student;
    }

    private Teacher createTeacherFixture() {
        Teacher teacher = new Teacher();
        teacher.setName("Undle Bae");
        teacher.setSubject("Computer Science");
        return teacher;
    }
}

- 위 테스트는 아래 4가지를 테스트한다.
A. 일반적인 CascadeType.ALL 동작

B. Delete And Insert Update 처리 확인

C. 리스트에서 첫번째 자식 객체를 제거한경우 처리 확인

4. 부모 제거시 동작 확인

6. 생성된 스키마 확인. 

    create table STUDENT (
        id bigint generated by default as identity,
        grade varchar(255),
        name varchar(255),
        TEACHER_ID bigint,
        primary key (id)
    )

    create table TEACHER (
        TEACHER_ID bigint generated by default as identity,
        name varchar(255),
        subject varchar(255),
        primary key (TEACHER_ID)
    )

    alter table STUDENT 
        add constraint UK_3leirxifnbbkn35r06ggygpj0  unique (name)

    alter table STUDENT 
        add constraint FK_kqtywag05gv4ixllub1d0ayp1 
        foreign key (TEACHER_ID) 
        references TEACHER


- 상기 내역을 보면 STUDENT에서 foreign key로 부모의 TEACHER_ID가 설정되었다. 

7. Cascade.ALL에 의한 부모 및 자식객체 저장 

    /* insert kr.co.unclebae.jpa.domain.Teacher
        */ insert
        into
            TEACHER
            (TEACHER_ID, name, subject)
        values
            (null, ?, ?)

    /* insert kr.co.unclebae.jpa.domain.Student
        */ insert
        into
            STUDENT
            (id, grade, name, TEACHER_ID)
        values
            (null, ?, ?, ?)

    /* insert kr.co.unclebae.jpa.domain.Student
        */ insert
        into
            STUDENT
            (id, grade, name, TEACHER_ID)
        values
            (null, ?, ?, ?)

- TEACHER 테이블에 에 인서트가 되고나서
- SUTDENT 테이블에 2건 인서트가 되었다.

    select
        teacher0_.TEACHER_ID as TEACHER_1_1_0_,
        teacher0_.name as name2_1_0_,
        teacher0_.subject as subject3_1_0_
    from
        TEACHER teacher0_
    where
        teacher0_.TEACHER_ID=?

    select
        students0_.TEACHER_ID as TEACHER_4_1_0_,
        students0_.id as id1_0_0_,
        students0_.id as id1_0_1_,
        students0_.grade as grade2_0_1_,
        students0_.name as name3_0_1_,
        students0_.TEACHER_ID as TEACHER_4_0_1_
    from
        STUDENT students0_
    where
        students0_.TEACHER_ID=?

- 해당 내역을 출력하기 위해서 TEACHER를 먼저 호출하고, STUDENT 를 TEACHER_ID로 호출하여 2건을 가져오고 있다.

- 출력 결과 : 
[GENERAL CascadeType.ALL]
SAVED TEACHER Teacher ID 1, name Undle Bae, subject Computer Science,
students [
{Student id 1, name STUDENT1, grade B-},
{Student id 2, name STUDENT2, grade A+},
]

8. Cascade.ALL, orphanRemove = true 처리 (삭제, 인서트, 업데이트)

    /* insert kr.co.unclebae.jpa.domain.Student
        */ insert
        into
            STUDENT
            (id, grade, name, TEACHER_ID)
        values
            (null, ?, ?, ?)

    /* update
        kr.co.unclebae.jpa.domain.Student */ update
            STUDENT
        set
            grade=?,
            name=?,
            TEACHER_ID=?
        where
            id=?

    /* delete kr.co.unclebae.jpa.domain.Student */ delete
        from
            STUDENT
        where
            id=?

- 우선 신규로 추가되는 STUDENT3이 인서트 된다.

- 다음으로 STUDENT1이 업데이트 된다. (ID가 같으므로 업데이트함)

- 마지막으로 STUDENT2가 삭제 된다.

# 중요한 것은 JPA는 ID값이 존재하면 업데이트를, ID가 없으면 인서트를 수행한다.
# orphanRemove를 통해서 부모와 연결이 끊어진 객체 STUDENT2는 트랜잭션이 종료 될때 제거된다.

- 처리 결과 : 
[CascadeType.REMOVE or CascadeType.ALL]
Delete And Merge Result Teacher ID 1, name Undle Bae, subject Computer Science,
students [
{Student id 1, name STUDENT1, grade F},
{Student id 3, name STUDENT3, grade A-},
]

9. orphanRemove = true설정후 부모 객체 삭제 

    /* delete kr.co.unclebae.jpa.domain.Student */ delete
        from
            STUDENT
        where
            id=?

- students의 첫번째 엘리먼트만 제거한경우에는 트랜잭션이 종료 되면서 해당 내역을 DB에서 제거한다.

- 처리결과 : 
[orphanRemove = true] RemoveFirstStudent Result Teacher ID 1, name Undle Bae, subject Computer Science, students [{Student id 3, name STUDENT3},]

    /* delete kr.co.unclebae.jpa.domain.Student */ delete 
        from
            STUDENT 
        where
            id=?

    /* delete kr.co.unclebae.jpa.domain.Teacher */ delete 
        from
            TEACHER 
        where
            TEACHER_ID=?

- 부모인 TEACHER객체를 제거하게 되면 부모를 잃은 자식 객체도 자동적으로 제거 된다. 
- 로그 : 
[orphanRemove = true] delete parent

10. 설정하기. 

10.1 hibernateConfig.xml설정하기. 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:jpa="http://www.springframework.org/schema/data/jpa"

       xmlns:context="http://www.springframework.org/schema/context" 

       xmlns:tx="http://www.springframework.org/schema/tx"

       xsi:schemaLocation="
          http://www.springframework.org/schema/beans 
          http://www.springframework.org/schema/beans/spring-beans.xsd 
          http://www.springframework.org/schema/context 
          http://www.springframework.org/schema/context/spring-context.xsd 
          http://www.springframework.org/schema/tx 
          http://www.springframework.org/schema/tx/spring-tx.xsd 
          http://www.springframework.org/schema/data/jpa 
          http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

   <jpa:repositories base-package="kr.co.unclebae.jpa.repository" />

    <tx:annotation-driven/>

    <context:component-scan base-package="kr.co.unclebae.jpa.service, 
kr.co.unclebae.jpa.repository"/>

    <bean id="dataSource" class="org.apache.tomcat.jdbc
.pool.DataSource">
        <property name="driverClassName" value="org.h2.Driver"/>
        <property name="url" value="jdbc:h2:mem:jpashop"/>
        <property name="username" value="sa"/>
        <property name="password" value=""/>
    </bean>

    <bean id="transactionManager" class="org.springframework
.orm.jpa.JpaTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean class="org.springframework.dao.annotation.
 PersistenceExceptionTranslationPostProcessor"/>

    <bean id="entityManagerFactory" class="org.springframework.
orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="packagesToScan" value="kr.co.unclebae.
jpa.domain"/> 
        <property name="jpaVendorAdapter">

            <bean class="org.springframework.orm.jpa.vendor.
HibernateJpaVendorAdapter"/>
        </property>
        <property name="jpaProperties"> 
            <props>
               <prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop>
               <prop key="hibernate.show_sql">true</prop> 
               <prop key="hibernate.format_sql">true</prop>
               <prop key="hibernate.use_sql_comments">true</prop>
               <prop key="hibernate.id.new_generator_mappings">true</prop>
               <prop key="hibernate.hbm2ddl.auto">create</prop>       
            </props>
        </property>
    </bean>

</beans>


10.2 POM 설정하기 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>JPATest</groupId>
    <artifactId>JPATest</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>JPATest</name>
    <packaging>war</packaging>

    <properties>

        <java.version>1.8</java.version>

        <spring-data-jpa.version>1.8.0.RELEASE</spring-data-jpa.version>
        <spring-framework.version>4.1.6.RELEASE</spring-framework.version>

        <querydsl.version>3.6.3</querydsl.version>
        <hibernate.version>4.3.10.Final</hibernate.version>
        <tomcat-jdbc.version>7.0.52</tomcat-jdbc.version>
        <h2db.version>1.4.187</h2db.version>

        <jsp.version>2.2</jsp.version>
        <jstl.version>1.2</jstl.version>
        <servlet.version>3.0.1</servlet.version>

        <logback.version>1.1.1</logback.version>
        <slf4j.version>1.7.6</slf4j.version>

        <junit.version>4.12</junit.version>

    </properties>

    <dependencies>

        <!-- 스프링 데이터 JPA -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>${spring-data-jpa.version}</version>
        </dependency>

        <!-- QueryDSL -->
        <dependency>
            <groupId>com.mysema.querydsl</groupId>
            <artifactId>querydsl-apt</artifactId>
            <version>${querydsl.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.mysema.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
            <version>${querydsl.version}</version>
        </dependency>

        <!-- 스프링 MVC -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>

        <!-- JPA, 하이버네이트 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>

        <!-- H2 데이터베이스 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>${h2db.version}</version>
            <scope>runtime</scope>
        </dependency>

        <!-- 커넥션 풀 -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jdbc</artifactId>
            <version>${tomcat-jdbc.version}</version>
            <scope>compile</scope>
        </dependency>

        <!-- WEB -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>${jstl.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
            <scope>provided</scope>
        </dependency>

        <!-- 로깅 SLF4J & LogBack -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
            <scope>runtime</scope>
        </dependency>

        <!-- 테스트 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring-framework.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <!-- Spring Data JPA 1.8이 스프링 4.0.9에 의존관계를 가지므로 스프링 버전을 직접 관리한다. -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${spring-framework.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>${spring-framework.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring-framework.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-orm</artifactId>
                <version>${spring-framework.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
                <version>${spring-framework.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>${spring-framework.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <path>/</path>
                    <uriEncoding>UTF-8</uriEncoding>
                </configuration>
            </plugin>

            <plugin>
                <groupId>com.mysema.maven</groupId>
                <artifactId>apt-maven-plugin</artifactId>
                <version>1.1.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>process</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>target/generated-sources/java</outputDirectory>
                            <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>