Spring Hibernate JPA配置示例

在此示例中,我们将学习使用JPA以及其他一些其他东西(例如验证和自定义属性编辑器)来配置Spring MVC和Hibernate。过去我被问过足够多的时间来编写本教程,所以就在这里。随时发表您的疑问,意见和建议。如果您想了解有关使用hibernate的会话工厂的信息,则可能需要阅读Spring和hibernate集成教程。

目录

1)应用程序概述
2)Spring的应用程序上下文配置
3)DAO类
4)Maven依赖项
5)其他应用程序文件

1)应用概述

该演示应用程序是一个员工管理屏幕,其中列出了数据库中的所有员工记录,以及一个可用于添加更多员工的表单。表单中添加了验证功能,因此,如果您尝试添加空白表单,则会收到验证错误。为了将记录存储在数据库中,使用了Hibernate JPA实现。前端是使用Spring MVC实现的。

应用程序屏幕截图 Hibernate jpa

此应用程序涉及的所有文件如下:

应用程序文件夹结构 Hibernate jpa

2)Spring的应用程序上下文配置

该文件是应用程序的核心部分,需要您的最大关注。

spring-servlet.xml

 
    <!-- It register the beans in context and scan the annotations inside beans and activate them -->
    <context:component-scan base-package="com.how2codex.demo" />
    
    <!-- This allow for dispatching requests to Controllers -->
    <mvc:annotation-driven />
     
    <!-- This helps in mapping the logical view names to directly view files under a certain pre-configured directory -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/" />
        <property name="suffix" value=".jsp" />
    </bean>
     
    <!-- This resolves messages from resource bundles for different locales -->
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="messages" />
    </bean>
    
    <!-- To validate the posted add employee form -->
    <bean id="employeeValidator" class="com.how2codex.demo.validator.EmployeeValidator" />
    
    <!-- This produces a container-managed EntityManagerFactory;
         rather than application-managed EntityManagerFactory as in case of LocalEntityManagerFactoryBean-->
    <bean id="entityManagerFactoryBean" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <!-- This makes /META-INF/persistence.xml is no longer necessary -->
      <property name="packagesToScan" value="com.how2codex.demo.model" />
      <!-- JpaVendorAdapter implementation for Hibernate EntityManager.
           Exposes Hibernate's persistence provider and EntityManager extension interface -->
      <property name="jpaVendorAdapter">
         <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
      </property>
      <property name="jpaProperties">
         <props>
            <prop key="hibernate.hbm2ddl.auto">validate</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
         </props>
      </property>
   </bean>
 
   <!-- Simple implementation of the standard JDBC DataSource interface,
        configuring the plain old JDBC DriverManager via bean properties -->
   <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="com.mysql.jdbc.Driver" />
      <property name="url" value="jdbc:mysql://localhost:3306/test" />
      <property name="username" value="root" />
      <property name="password" value="password" />
   </bean>
    
    <!-- This transaction manager is appropriate for applications that use a single JPA EntityManagerFactory for transactional data access.
        JTA (usually through JtaTransactionManager) is necessary for accessing multiple transactional resources within the same transaction. -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
      <property name="entityManagerFactory" ref="entityManagerFactoryBean" />
   </bean>
   
   <!-- responsible for registering the necessary Spring components that power annotation-driven transaction management;
        such as when @Transactional methods are invoked -->
   <tx:annotation-driven />
   
</beans>

让我们记下几个要点。

  • context:component-scan:在上下文中注册bean;并且还会扫描bean内的注解并激活它们。这样context:component-scancontext:annotation-config也可以,但是它还会扫描软件包并在应用程序上下文中注册Bean。

    阅读更多:context:annotation-config与context:component-scan

  • MVC:注解驱动:标签基本设置你的Spring上下文允许调度使用注解带注解的控制器方法的要求,例如@RequestMapping@ExceptionHandler和其他人。

    阅读更多:Spring MVC Config

  • InternalResourceViewResolver:视图模板通过将逻辑视图名称映射到直接在特定预配置目录下的视图文件,根据返回的逻辑视图名称决定应渲染哪个视图。

    阅读更多:InternalResourceViewResolver配置示例

  • EntityManagerFactoryBeanLocalEntityManagerFactoryBean产生一个应用程序管理的, EntityManagerFactoryLocalContainerEntityManagerFactoryBean产生一个容器管理的 EntityManagerFactory。它支持到现有JDBC数据源的链接,同时支持本地和全局事务。
  • JpaTransactionManager:此事务管理器适用于使用单个JPA EntityManagerFactory进行事务数据访问的应用程序。JTA(通常通过JtaTransactionManager)对于访问同一事务中的多个事务资源是必需的。请注意,您需要相应地配置JPA提供程序,以使其参与JTA事务。当然,JtaTransactionManager确实需要一个完整的支持JTA的应用程序服务器,而不是像Tomcat这样的普通servlet引擎。
  • tx:annotation-driven:基于注解启用事务行为的配置,例如@Transactional。该@EnableTransactionManagement注解提供同样的支持,如果你使用的是基于Java的配置。去做这个。只需将注解添加到@Configuration类。

3)DAO课程

接下来的重要类是DAO类,它使用实体管理器使用 Hibernate 实体执行CRUD操作,并指定通过@Transactional注解支持事务的方法。在我们的案例中,我们@Transactional在类级别应用了注解,使所有公共方法都具有事务性。

EmployeeDAO.java

public interface EmployeeDAO
{
    public List<EmployeeEntity> getAllEmployees();
    public List<DepartmentEntity> getAllDepartments();
    public void addEmployee(EmployeeEntity employee);
}

EmployeeDAOImpl.java

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.how2codex.demo.model.DepartmentEntity;
import com.how2codex.demo.model.EmployeeEntity;
@Repository
@Transactional
public class EmployeeDAOImpl implements EmployeeDAO
{
    @PersistenceContext
    private EntityManager manager;
    
    public List<EmployeeEntity> getAllEmployees()
    {
        List<EmployeeEntity> employees = manager.createQuery("Select a From EmployeeEntity a", EmployeeEntity.class).getResultList();
        return employees;
    }
    
    public List<DepartmentEntity> getAllDepartments()
    {
        List<DepartmentEntity> depts = manager.createQuery("Select a From DepartmentEntity a", DepartmentEntity.class).getResultList();
        return depts;
    }
    
    public DepartmentEntity getDepartmentById(Integer id)
    {
        return manager.find(DepartmentEntity.class, id);
    }
    
    public void addEmployee(EmployeeEntity employee)
    {
        //Use null checks and handle them
        employee.setDepartment(getDepartmentById(employee.getDepartment().getId()));
        manager.persist(employee);
    }
}
使用代理时,应仅将@Transactional注解应用于具有公共可见性的方法。如果使用@Transactional注解对受保护的,私有的或程序包可见的方法进行注解,则不会引发任何错误,但是带注解的方法不会显示已配置的事务设置。如果需要注解非公共方法,请考虑使用AspectJ。

@PersistenceContext表示对容器管理的容器EntityManager及其关联的持久性上下文的依赖。@Repository通常应用于DAO层。

阅读更多:@ Component,@ Repository,@ Service和@Controller注解

4)Maven依赖

最后,您应该在此示例中感兴趣的是spring,hibernate和JPA扩展的maven依赖关系。这是此示例的所有依赖项。

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!-- Spring MVC support -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>4.1.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>4.1.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>4.1.4.RELEASE</version>
    </dependency>
    <!-- Tag libs support for view layer -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>taglibs</groupId>
        <artifactId>standard</artifactId>
        <version>1.1.2</version>
        <scope>runtime</scope>
    </dependency>
    
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.0.0.GA</version>
    </dependency>
    
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>4.0.1.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>4.2.0.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate.common</groupId>
        <artifactId>hibernate-commons-annotations</artifactId>
        <version>4.0.1.Final</version>
        <classifier>tests</classifier>
    </dependency>
    
    <dependency>
        <groupId>org.hibernate.javax.persistence</groupId>
        <artifactId>hibernate-jpa-2.0-api</artifactId>
        <version>1.0.1.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>4.0.1.Final</version>
    </dependency>
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.0.0.GA</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.6.4</version>
    </dependency>
    <dependency>
        <groupId>org.jboss.logging</groupId>
        <artifactId>jboss-logging</artifactId>
        <version>3.1.0.CR2</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.6.4</version>
    </dependency>
    
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.10</version>
    </dependency>
</dependencies>

5)其他应用文件

现在,列出本示例中使用的其他文件。

web.xml

<web-app id="WebApp_ID" version="2.4"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
    
    <display-name>Spring Hibernate JPA Hello World Application</display-name>
    
    <!-- Configuration file for the root application context -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring-servlet.xml
        </param-value>
    </context-param>
    
    <!-- Configuration for the DispatcherServlet -->
    <servlet>
        <servlet-name>spring</servlet-name>
            <servlet-class>
                org.springframework.web.servlet.DispatcherServlet
            </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

listEmployeeView.jsp

<%@ page contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Employee Management</title>
    <style>
    .error
    {
        color: #ff0000;
        font-weight: bold;
    }
    #listOfEmployees tr:first-child{
        font-weight: bold;
    }
    </style>
</head>
<body>
    
    <h2><spring:message code="lbl.page.list" text="lbl.page.list" /></h2>
    <br/>
    
    <table id="listOfEmployees" border="1">
    <tr>
        <td>ID</td>
        <td>First Name</td>
        <td>Last Name</td>
        <td>Email</td>
        <td>Department</td>
      </tr>
    <c:forEach items="${allEmployees}" var="employee">   
      <tr>
        <td>${employee.id}</td>
        <td>${employee.firstName}</td>
        <td>${employee.lastName}</td>
        <td>${employee.email}</td>
        <td>${employee.department.name}</td>
      </tr>
    </c:forEach>
    </table>
    <h2><spring:message code="lbl.page" text="Add New Employee" /></h2>
    <br/>
    <form:form method="post" modelAttribute="employee">
        <table>
            <tr>
                <td><spring:message code="lbl.firstName" text="First Name" /></td>
                <td><form:input path="firstName" /></td>
                <td><form:errors path="firstName" cssClass="error" /></td>
            </tr>
            <tr>
                <td><spring:message code="lbl.lastName" text="Last Name" /></td>
                <td><form:input path="lastName" /></td>
                <td><form:errors path="lastName" cssClass="error" /></td>
            </tr>
            <tr>
                <td><spring:message code="lbl.email" text="Email Id" /></td>
                <td><form:input path="email" /></td>
                <td><form:errors path="email" cssClass="error" /></td>
            </tr>
            <tr>
                <td><spring:message code="lbl.department" text="Department" /></td>
                <td><form:select path="department" items="${allDepartments}" itemValue="id" itemLabel="name" /></td>
                <td><form:errors path="department" cssClass="error" /></td>
            </tr>
            <tr>
                <td colspan="3"><input type="submit" value="Add Employee"/></td>
            </tr>
        </table>
    </form:form>
</body>
</html>


messages.properties

lbl.page=Add New Employee
lbl.page.list=List of Employees
lbl.firstName=First Name
lbl.lastName=Last Name
lbl.email=Email Id
error.firstName=First Name can not be blank
error.lastName=Last Name can not be blank
error.email=Email Id can not be blank

EmployeeValidator.java

@Component
public class EmployeeValidator implements Validator {
    public boolean supports(Class clazz) {
        return EmployeeEntity.class.isAssignableFrom(clazz);
    }
    public void validate(Object target, Errors errors)
    {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "error.firstName", "First name is required.");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "error.lastName", "Last name is required.");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "error.email", "Email is required.");
    }
}

EmployeeManager.java

public interface EmployeeManager
{
    public List<EmployeeEntity> getAllEmployees();
    public List<DepartmentEntity> getAllDepartments();
    public void addEmployee(EmployeeEntity employee);
}

EmployeeManagerImpl.java

@Service
public class EmployeeManagerImpl implements EmployeeManager {
    @Autowired
    EmployeeDAO dao;
    
    public List<EmployeeEntity> getAllEmployees() {
        return dao.getAllEmployees();
    }
    public List<DepartmentEntity> getAllDepartments() {
        return dao.getAllDepartments();
    }
    public void addEmployee(EmployeeEntity employee) {
        dao.addEmployee(employee);
    }
}


EmployeeEntity.java

@Entity
@Table (name="employee")
public class EmployeeEntity implements Serializable
{
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue
    private Integer id;
    
    @NotEmpty
    private String firstName;
    private String lastName;
    private String email;
    
    @NotNull
    @ManyToOne
    private DepartmentEntity department;
    
    public EmployeeEntity() {}
     
    public EmployeeEntity(String name, DepartmentEntity department) {
        this.firstName = name;
        this.department = department;
    }
     
    public EmployeeEntity(String name) {
        this.firstName = name;
    }
    //Setters and Getters
    @Override
    public String toString() {
        return "EmployeeVO [id=" + id + ", firstName=" + firstName
                + ", lastName=" + lastName + ", email=" + email
                + ", department=" + department + "]";
    }
}

DepartmentEntity.java

@Entity
@Table (name="department")
public class DepartmentEntity implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @Id
    @GeneratedValue
    private Integer id;
    private String name;
    
    public DepartmentEntity(){
    }
    public DepartmentEntity(Integer id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
    
    @OneToMany(mappedBy="department",cascade=CascadeType.PERSIST)
    private List<EmployeeEntity> employees = new ArrayList<EmployeeEntity>();
    
    //Setters and Getters
    
    @Override
    public String toString() {
        return "DepartmentVO [id=" + id + ", name=" + name + "]";
    }
}

EmployeeController.java

@Controller
@RequestMapping("/employee-module")
@SessionAttributes("employee")
public class EmployeeController
{
    @Autowired
    EmployeeManager manager;
    private Validator validator;
    //Bind custom validator for submitted form
    public EmployeeController()
    {
        ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
        validator = validatorFactory.getValidator();
    }
    
    /**
     * Bind DepartmentEditor to DepartmentEntity; Look at JSP file for clearer picture
     * */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(DepartmentEntity.class, new DepartmentEditor());
    }
    /**
     * Bind all departments
     * */
    @ModelAttribute("allDepartments")
    public List<DepartmentEntity> populateDepartments()
    {
        List<DepartmentEntity> departments = manager.getAllDepartments();
        return departments;
    }
    
    /**
     * Bind all employees list
     * */
    @ModelAttribute("allEmployees")
    public List<EmployeeEntity> populateEmployees()
    {
        List<EmployeeEntity> employees = manager.getAllEmployees();
        return employees;
    }
    
    /**
     * Method will be called in initial page load at GET /employee-module
     * */
    @RequestMapping(method = RequestMethod.GET)
    public String setupForm(Model model)
    {
        EmployeeEntity employeeVO = new EmployeeEntity();
        model.addAttribute("employee", employeeVO);
        return "listEmployeeView";
    }
    /**
     * Method will be called on submitting add employee form POST /employee-module
     * */
    @RequestMapping(method = RequestMethod.POST)
    public String submitForm(@ModelAttribute("employee") EmployeeEntity employeeVO,
            BindingResult result, SessionStatus status) {
        Set<ConstraintViolation<EmployeeEntity>> violations = validator.validate(employeeVO);
        
        for (ConstraintViolation<EmployeeEntity> violation : violations)
        {
            String propertyPath = violation.getPropertyPath().toString();
            String message = violation.getMessage();
            // Add JSR-303 errors to BindingResult
            // This allows Spring to display them in view via a FieldError
            result.addError(new FieldError("employee", propertyPath, "Invalid "+ propertyPath + "(" + message + ")"));
        }
        if (result.hasErrors()) {
            return "listEmployeeView";
        }
        // Store the employee information in database
        manager.addEmployee(employeeVO);
        
        // Mark Session Complete and redirect to URL so that page refresh do not re-submit the form
        status.setComplete();
        return "redirect:employee-module";
    }
}

请随时提出您的问题和建议。

saigon has written 1440 articles

Leave a Reply