Spring –持久层–编写实体并配置Hibernate

欢迎来到本教程的第二部分。 当您看到本文有多长时间时,请不要惊慌–我向您保证,这主要是简单的POJO和一些生成的代码。

在开始之前,我们需要更新我们的Maven依赖项,因为我们现在将使用Hibernate和Spring。 将以下依赖项添加到pom.xml中

<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-entitymanager</artifactId>
   <version>3.6.8.Final</version>
  </dependency>
                <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.6</version>
  </dependency>
  
  <!-- spring framework -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>
                <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-orm</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>
                <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>

如果您是Maven的新手,您可能现在想知道-您怎么知道这些? 我在哪里可以买到? 好吧,只需转到http://mvnrepository.com/并输入您要搜索的啤酒。 您将获得有关Maven依赖项的完整代码。 如果您曾经尝试在不使用Maven的情况下自己组装Spring或Hibernate应用程序,那么您可能知道它是多么痛苦。 使用Maven,事情变得简单得多。

还要注意,我们包括了对MySQL连接器的依赖。 如果您决定使用其他数据库,请不要忘记更改它。

使用Hibernate,我们有2种选择如何将POJO变成实体。 我们要么使用XML并创建映射文件 ,要么将一些元信息放入我们的代码(java批注)中。 有些人对此感到恐惧,并认为这是与框架的结合。 的确,您在类路径中将需要javax.persistence批注,但我们不会实现接口或扩展框架类。 我们将只在代码中添加一些元信息,而POJO仍然只是带有一些额外信息的POJO。

现在,我们将POJO转换为实体。 我们将需要进行以下更改:

  • 为Hibernate添加默认的无参数构造函数
  • 为字段创建获取器和设置器
  • 添加equalshashCode方法。
  • 添加持久性注释。 请注意,我们还使用@Table批注来区分Java和SQL命名约定。
  • 添加ID字段。 这些将是我们关系数据库中的主键。

这是很多样板代码,因此让您的IDE帮您。 大多数现代IDE都会为您生成构造函数,getter,setter,equals和hashCode。

package org.timesheet.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = 'employee')
public class Employee {

 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Long id;
 private String name;
 private String department;
 
 public Employee() {
 }

 public Employee(String name, String department) {
  this.name = name;
  this.department = department;
 }
 
 public String getName() {
  return name;
 }
 
 public String getDepartment() {
  return department;
 }
 
 public Long getId() {
  return id;
 }
 
 public void setId(Long id) {
  this.id = id;
 }

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

 public void setDepartment(String department) {
  this.department = department;
 }

 @Override
 public String toString() {
  return 'Employee [id=' + id + ', name=' + name + ', department='
    + department + ']';
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result
    + ((department == null) ? 0 : department.hashCode());
  result = prime * result + ((id == null) ? 0 : id.hashCode());
  result = prime * result + ((name == null) ? 0 : name.hashCode());
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj) {
   return true;
  }
  if (obj == null) {
   return false;
  }
  if (!(obj instanceof Employee)) {
   return false;
  }
  Employee other = (Employee) obj;
  if (department == null) {
   if (other.department != null) {
    return false;
   }
  } else if (!department.equals(other.department)) {
   return false;
  }
  if (id == null) {
   if (other.id != null) {
    return false;
   }
  } else if (!id.equals(other.id)) {
   return false;
  }
  if (name == null) {
   if (other.name != null) {
    return false;
   }
  } else if (!name.equals(other.name)) {
   return false;
  }
  return true;
 }
 
}
package org.timesheet.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = 'manager')
public class Manager {
 
 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Long id;
 private String name;
 
 public Manager() {
 }

 public Manager(String name) {
  this.name = name;
 }
 
 public String getName() {
  return name;
 }

 public Long getId() {
  return id;
 }

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

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

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((id == null) ? 0 : id.hashCode());
  result = prime * result + ((name == null) ? 0 : name.hashCode());
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj) {
   return true;
  }
  if (obj == null) {
   return false;
  }
  if (!(obj instanceof Manager)) {
   return false;
  }
  Manager other = (Manager) obj;
  if (id == null) {
   if (other.id != null) {
    return false;
   }
  } else if (!id.equals(other.id)) {
   return false;
  }
  if (name == null) {
   if (other.name != null) {
    return false;
   }
  } else if (!name.equals(other.name)) {
   return false;
  }
  return true;
 }

 @Override
 public String toString() {
  return 'Manager [id=' + id + ', name=' + name + ']';
 }
 
}
package org.timesheet.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table(name='timesheet')
public class Timesheet {

 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Long id;
 
 @OneToOne
 @JoinColumn(name = 'employee_id')
 private Employee who;
 
 @OneToOne
 @JoinColumn(name = 'task_id')
 private Task task;
 private Integer hours;
 
 public Timesheet() {
 }
 
 public Timesheet(Employee who, Task task, Integer hours) {
  this.who = who;
  this.task = task;
  this.hours = hours;
 }

 public Employee getWho() {
  return who;
 }

 public Task getTask() {
  return task;
 }
 
 public Integer getHours() {
  return hours;
 }
 
 public Long getId() {
  return id;
 }

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

 public void setWho(Employee who) {
  this.who = who;
 }

 public void setTask(Task task) {
  this.task = task;
 }

 public void setHours(Integer hours) {
  this.hours = hours;
 }

 /**
  * Manager can alter hours before closing task
  * @param hours New amount of hours
  */
 public void alterHours(Integer hours) {
  this.hours = hours;
 }

 @Override
 public String toString() {
  return 'Timesheet [id=' + id + ', who=' + who + ', task=' + task
    + ', hours=' + hours + ']';
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((hours == null) ? 0 : hours.hashCode());
  result = prime * result + ((id == null) ? 0 : id.hashCode());
  result = prime * result + ((task == null) ? 0 : task.hashCode());
  result = prime * result + ((who == null) ? 0 : who.hashCode());
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj) {
   return true;
  }
  if (obj == null) {
   return false;
  }
  if (!(obj instanceof Timesheet)) {
   return false;
  }
  Timesheet other = (Timesheet) obj;
  if (hours == null) {
   if (other.hours != null) {
    return false;
   }
  } else if (!hours.equals(other.hours)) {
   return false;
  }
  if (id == null) {
   if (other.id != null) {
    return false;
   }
  } else if (!id.equals(other.id)) {
   return false;
  }
  if (task == null) {
   if (other.task != null) {
    return false;
   }
  } else if (!task.equals(other.task)) {
   return false;
  }
  if (who == null) {
   if (other.who != null) {
    return false;
   }
  } else if (!who.equals(other.who)) {
   return false;
  }
  return true;
 }
}

最后,这是我们需要同时使用@ManyToMany映射的Task实体。 这是因为一名雇员可以从事多个任务,而一项任务可以分配多个雇员。 我们使用@JoinTable和@JoinColumn批注定义了m:n的外观。

package org.timesheet.domain;

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

@Entity
@Table(name = 'task')
public class Task {

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

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = 'task_employee',
            joinColumns = {@JoinColumn(name = 'task_id')},
            inverseJoinColumns = {@JoinColumn(name = 'employee_id')}
    )
    private List<Employee> assignedEmployees = new ArrayList<Employee>();

    @OneToOne
    @JoinColumn(name = 'manager_id')
    private Manager manager;

    private String description;
    boolean completed;

    public Task() {
    }

    public Task(String description, Manager manager, Employee... employees) {
        this.description = description;
        this.manager = manager;
        assignedEmployees.addAll(Arrays.asList(employees));
        completed = false;
    }

    public Manager getManager() {
        return manager;
    }

    public List<Employee> getAssignedEmployees() {
        return assignedEmployees;
    }

    public void addEmployee(Employee e) {
        assignedEmployees.add(e);
    }

    public void removeEmployee(Employee e) {
        assignedEmployees.remove(e);
    }

    public Long getId() {
        return id;
    }

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

    public boolean isCompleted() {
        return completed;
    }

    public void setCompleted(boolean completed) {
        this.completed = completed;
    }

    public void setAssignedEmployees(List<Employee> assignedEmployees) {
        this.assignedEmployees = assignedEmployees;
    }

    public void setManager(Manager manager) {
        this.manager = manager;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        Task task = (Task) o;

        if (completed != task.completed) {
            return false;
        }
        if (description != null ? !description.equals(task.description) : task.description != null) {
            return false;
        }
        if (id != null ? !id.equals(task.id) : task.id != null) {
            return false;
        }
        if (manager != null ? !manager.equals(task.manager) : task.manager != null) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result = id != null ? id.hashCode() : 0;
        result = 31 * result + (manager != null ? manager.hashCode() : 0);
        result = 31 * result + (description != null ? description.hashCode() : 0);
        result = 31 * result + (completed ? 1 : 0);
        return result;
    }

    @Override
    public String toString() {
        return 'Task{' +
                'id=' + id +
                ', assignedEmployees=' + assignedEmployees +
                ', manager=' + manager +
                ', description='' + description + '\'' +
                ', completed=' + completed +
                '}';
    }
}

因此,我们实际上并没有做任何特别的建模。 如果您喜欢一些UML,请看下图,关系与以前相同。

好的,我们有实体,现在让我们创建数据库。 选择一些数据库管理工具(即使是普通终端也可以)并创建时间表数据库(默认情况下,mysql将在Mac OS X上安装到/ usr / local / mysql / bin / mysql):

$ mysql -u root
mysql > create database timesheet;

如果您可能在不了解之前就已经配置过Hibernate,那么在处理SessionFactory时就需要很多文件和样板代码。 使用Spring,这些事情要简单得多。

现在,我们将创建我们的第一个Spring Bean配置文件 -该文件在其中注册Spring容器的bean。 如果我不得不向根本不了解Spring的人解释这个文件是什么,那是Spring容器可以找到对象的神奇包。

现代IDE将帮助您正确使用所有XML名称空间,例如,您可以从STS向导中查看图片。 NetBeans具有类似的功能,IntelliJ可以动态解析名称空间。

命名配置文件persistence-beans.xml,然后将其放在src / main / resources文件夹下。

因此,设置Hibernate,事务,批注配置等就像在XML文件中创建几个bean一样简单。 另外,我们可以将Java Config用于Spring,但是XML config仍然使用更多,因此我们将坚持使用这些配置。 我不想阻止您使用Java Config! XML config 现在更受欢迎,但是我不能保证在接下来的几年中。
我已经评论了每个bean,以确保您在继续之前了解我们在这里所做的事情。 如果要直观地了解Bean之间的连接,则可以再次使用一些工具-在STS中将其称为Bean Graph,在IntelliJ中将其称为Dependencies。 您可以在下面的图片中看到依赖项示例。

<?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: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-3.1.xsd
  http://www.springframework.org/schema/tx 
  http://www.springframework.org/schema/tx/spring-tx-3.1.xsd'>
 
 <!-- we can use annotations -->
 <context:annotation-config />  
 
 <!-- package to look for annotated classes -->
 <context:component-scan base-package='org.timesheet.service.impl' />
 
 <!-- we will manage transactions with annotations -->
 <tx:annotation-driven />

 <!-- data source for our database -->
 <bean id='dataSource' 
  class='org.springframework.jdbc.datasource.DriverManagerDataSource'>
  <property name='driverClassName' 
   value='com.mysql.jdbc.jdbc2.optional.MysqlDataSource' />
  <property name='url' value='jdbc:mysql://localhost/timesheet' />
  <property name='username' value='root' />
  <property name='password' value='' />
 </bean>
 
 <!-- configure hibernate session factory -->
 <bean id='sessionFactory'
  class='org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean'>
  <property name='dataSource' ref='dataSource' />
  <property name='annotatedClasses' >
   <list>
    <value>org.timesheet.domain.Employee</value>
    <value>org.timesheet.domain.Manager</value>
    <value>org.timesheet.domain.Task</value>
    <value>org.timesheet.domain.Timesheet</value>
   </list>
  </property>
  <property name='hibernateProperties'>
   <props>
    <prop key='dialect'>org.hibernate.dialect.MySQL5InnoDBDialect</prop>
    <prop key='hibernate.show_sql'>true</prop>
    <prop key='hibernate.hbm2ddl.auto'>update</prop>
   </props>
  </property> 
 </bean>
 
</beans>

好的,那是很多配置,对吗? 不好的是,我们已经将实体的名称作为纯文本放置到XML中,因此它不是友好的重构形式。 但我认为对于本教程来说是可以接受的 :) 让我们为Hibernate编写集成测试,以便我们知道一切都已正确设置。

package org.timesheet.integration;

import static org.junit.Assert.*;

import org.hibernate.SessionFactory;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;

@ContextConfiguration(locations = '/persistence-beans.xml')
public class HibernateConfigurationTest extends AbstractJUnit4SpringContextTests {
 
 @Autowired
 private SessionFactory sessionFactory;

 @Test
 public void testHibernateConfiguration() {
  // Spring IOC container instantiated and prepared sessionFactory
  assertNotNull (sessionFactory); 
 }

}

我要你在这里注意两件事。 首先,我们扩展AbstractJUnit4SpringContextTests类。 我们告诉它应该在哪里寻找使用Spring bean的实际XML配置。 否则,我们将不得不自己创建Spring容器,这意味着需要更多样板代码。

其次,我们使用@Autowired批注。 这意味着我们不会使用新的运算符手动创建SessionFactory实例,而是将其通过Spring容器自动装配(注入)! 这是Spring容器最重要的目的之一-依赖于接口注入实现而不是手工创建它们。
一切现在都应该正常工作,我认为这部分就足够了。

如果您愿意,可以检查普通的SQL,并在此处查看表,请按照以下步骤进行操作:

mysql> use timesheet;
mysql> show tables;
+---------------------+
| Tables_in_timesheet |
+---------------------+
| employee            |
| manager             |
| task                |
| task_employee       |
| timesheet           |
+---------------------+
5 rows in set (0.00 sec)

参考: 第2部分–持久层–vrtoonjava博客的JCG合作伙伴 Michal Vrtiak 编写实体并配置Hibernate


翻译自: https://www.javacodegeeks.com/2012/09/spring-persistence-layer-writing.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值