使用JPA为应用添加持久层
第一步:创建基类
让我们首先为我们的域模型创建一个基类。这通重点内容常被认为是最佳实践,因为它为我们提供了一个中心位置,可以在以后添加要在所有域模型对象之间共享的通用功能。为此,从项目的上下文菜单项中选择New> Class,weatherapp并提供以下详细信息:
• 包名: com.sap.hana.cloud.samples.weatherapp.model
• 类名: BaseObject
第二步:替换代码
该代码就是BaseObject的内容
package com.sap.hana.cloud.samples.weatherapp.model;
import java.util.Date;
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
/**
* Base class for all domain model objects.
*/
@MappedSuperclass
public abstract class BaseObject
{
/**
* The (globally unique) ID of the object.
*/
@Id
@Column(name="GUID", length = 36)
private String guid = UUID.randomUUID().toString();
/**
* The {@link Date} the object was created at.
*/
@Temporal(TemporalType.TIMESTAMP)
@Column(name="CREATION_DATE", updatable = false)
private Date createdAt = null;
/**
* The {@link Date} the object was last modified at.
*/
@Temporal(TemporalType.TIMESTAMP)
@Column(name="MODIFICATION_DATE")
private Date lastModifiedAt = null;
/**
* ID of the user who created the object.
*/
@Column(name="CREATED_BY", updatable = false, length = 20)
private String createdBy = null;
/**
* ID of the user who was the last to modify the object.
*/
@Column(name="MODIFIED_BY", length = 20)
private String lastModifiedBy = null;
/**
* Life-cycle event callback, which automatically sets the last modification date.
*/
@PreUpdate
protected void updateAuditInformation()
{
lastModifiedAt = new Date();
// TODO - obtain currently logged-on user
}
/**
* Life-cycle event callback, which automatically creates a unique ID for the object
* and populates its audit information.
*/
@PrePersist
protected void generateAuditInformation()
{
final Date now = new Date();
createdAt = now;
lastModifiedAt = now;
// TODO - obtain currently logged-on user
}
public String getGuid()
{
return this.guid;
}
public void setGuid(String guid)
{
this.guid = guid;
}
public Date getCreatedAt()
{
return this.createdAt;
}
public void setCreatedAt(Date createdAt)
{
this.createdAt = createdAt;
}
public Date getLastModifiedAt()
{
return this.lastModifiedAt;
}
public void setLastModifiedAt(Date lastModifiedAt)
{
this.lastModifiedAt = lastModifiedAt;
}
public String getCreatedBy()
{
return this.createdBy;
}
public void setCreatedBy(String createdBy)
{
this.createdBy = createdBy;
}
public String getLastModifiedBy()
{
return this.lastModifiedBy;
}
public void setLastModifiedBy(String lastModifiedBy)
{
this.lastModifiedBy = lastModifiedBy;
}
}
–第三步:创建另一个Java类
接下来,FavoriteCity.java使用相同的过程创建另一个Java类():
• 包名: com.sap.hana.cloud.samples.weatherapp.model
类名: FavoriteCity
替换该类的代码
package com.sap.hana.cloud.samples.weatherapp.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Model object representing a single {@link FavoriteCity} instance.
*
* @author Matthias Steiner
* @version 0.1
*/
@Entity
@Table(name = "WEATHERAPP_CITY")
@NamedQueries({@NamedQuery(name = "FavoriteCities", query = "SELECT c FROM FavoriteCity c"),
@NamedQuery(name = "FavoriteCityById", query = "SELECT c FROM FavoriteCity c WHERE c.id = :id")})
@XmlRootElement(name = "city")
@XmlAccessorType(XmlAccessType.FIELD)
public class FavoriteCity extends BaseObject implements Serializable
{
/**
* The <code>serialVersionUID</code> of the {@link FavoriteCity} class.
*/
private static final long serialVersionUID = 1L;
@Column(name="ID", length = 36, nullable=true)
String id = null;
@Column(name="NAME", length = 128, nullable=true)
String name = null;
@Column(name="COUNTRY", length = 2, nullable=true)
String countryCode = null;
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getCountryCode()
{
return countryCode;
}
public void setCountryCode(String countryCode)
{
this.countryCode = countryCode;
}
}
第四步:创建配置文件
接下来,我们需要为持久层创建配置文件。根据Maven约定,这些非源代码工件应位于名为的单独源代码文件夹中:src/main/resources。因此,让我们通过Project Explorer中Java Resources节点上相应的上下文菜单条目创建该源文件夹:New> Source Folder。提供以下信息:
• 项目名: weatherapp
• 文件夹名称: src/main/resources
第五步:创建文件夹
打开此新创建的源文件夹的上下文菜单,然后选择“ 新建”>“其他”选项,然后选择“ 文件夹”选项。命名新文件夹META-INF(所有大写!),然后单击“ 完成”。
第六步:创建持久性xml文件
打开新创建的META-INF文件夹的上下文菜单,然后选择“ 新建”>“文件”。命名新文件persistence.xml,然后单击“ 完成”。
将以下XML内容复制并粘贴到persistence.xml文件中:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="application" transaction-type="RESOURCE_LOCAL">
<provider>
org.eclipse.persistence.jpa.PersistenceProvider
</provider>
<class>
com.sap.hana.cloud.samples.weatherapp.model.BaseObject
</class>
<class>
com.sap.hana.cloud.samples.weatherapp.model.FavoriteCity
</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="eclipselink.ddl-generation" value="create-tables"/>
</properties>
</persistence-unit>
</persistence>
第七步:添加依赖项
接下来,我们需要为我们的pom.xml文件添加更多依赖项。在这种情况下,最重要的依赖是EclipseLink(我们选择的JPA实现)。但是,我们还需要声明Derby DB和Jackson的依赖关系(将数据转换为JSON所需的序列化框架,反之亦然。)
复制代码
<!-- EclipseLink (and JPA) -->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.1.0</version>
</dependency>
<!-- Derby -->
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derbyclient</artifactId>
<version>10.9.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.9.1.0</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>${org.codehaus.jackson-version}</version>
</dependency>
我们还需要将Jackson版本作为属性添加到pom.xml文件的属性部分(正如我们在上一节中所做的那样)并保存您的更改
<org.codehaus.jackson-version>1.9.9</org.codehaus.jackson-version>
第八步:创建CRUD服务
下一步是创建相应的CRUD服务。为此,请创建一个包含以下详细信息的新类:
• 包名: com.sap.hana.cloud.samples.weatherapp.api
• 类名: FavoriteCityService
该类的代码内容
package com.sap.hana.cloud.samples.weatherapp.api;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.sql.DataSource;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.SecurityContext;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import com.sap.hana.cloud.samples.weatherapp.model.FavoriteCity;
/**
* {@link FavoriteCityService}
*
* @author Matthias Steiner
* @version 0.1
*/
@Path("/cities")
@Produces({ MediaType.APPLICATION_JSON })
public class FavoriteCityService
{
@SuppressWarnings("unchecked")
@GET
@Path("/")
public List<FavoriteCity> getFavoriteCities()
{
List<FavoriteCity> retVal = null;
EntityManager em = this.getEntityManagerFactory().createEntityManager();
retVal = em.createNamedQuery("FavoriteCities").getResultList();
return retVal;
}
@GET
@Path("/{id}")
public FavoriteCity getFavoriteCity(@PathParam(value = "id") String id)
{
FavoriteCity retVal = null;
EntityManager em = this.getEntityManagerFactory().createEntityManager();
try
{
Query query = em.createNamedQuery("FavoriteCityById");
query.setParameter("id", id);
retVal = (FavoriteCity) query.getSingleResult();
}
catch(Exception ex)
{
ex.printStackTrace();
}
finally
{
em.close();
}
return retVal;
}
@SuppressWarnings("unchecked")
@POST
@Path("/")
public List<FavoriteCity> addFavoriteCity(FavoriteCity city)
{
List<FavoriteCity> retVal = null;
EntityManager em = this.getEntityManagerFactory().createEntityManager();
try
{
em.getTransaction().begin();
em.persist(city);
em.getTransaction().commit();
retVal = em.createNamedQuery("FavoriteCities").getResultList();
}
catch(Exception ex)
{
ex.printStackTrace();
}
finally
{
em.close();
}
return retVal;
}
@SuppressWarnings("unchecked")
@DELETE
@Path("/{id}")
public List<FavoriteCity> removeFavoriteCity(@PathParam(value = "id") String id)
{
List<FavoriteCity> retVal = null;
EntityManager em = this.getEntityManagerFactory().createEntityManager();
try
{
Query query = em.createNamedQuery("FavoriteCityById");
query.setParameter("id", id);
FavoriteCity city = (FavoriteCity) query.getSingleResult();
if (city != null)
{
em.getTransaction().begin();
em.remove(city);
em.getTransaction().commit();
}
retVal = em.createNamedQuery("FavoriteCities").getResultList();
}
catch(Exception ex)
{
ex.printStackTrace();
}
finally
{
em.close();
}
return retVal;
}
/**
* Returns the <code>DefaultDB</code> {@link DataSource} via JNDI.
*
* @return <code>DefaultDB</code> {@link DataSource}
*/
protected DataSource getDataSource()
{
DataSource retVal = null;
try
{
InitialContext ctx = new InitialContext();
retVal = (DataSource) ctx.lookup("java:comp/env/jdbc/DefaultDB");
}
catch (NamingException ex)
{
ex.printStackTrace();
}
return retVal;
}
/**
* Returns the {@link EntityManagerFactory}.
*
* @return The {@link EntityManagerFactory}
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected EntityManagerFactory getEntityManagerFactory()
{
EntityManagerFactory retVal = null;
try
{
Map properties = new HashMap();
DataSource ds = this.getDataSource();
properties.put(PersistenceUnitProperties.NON_JTA_DATASOURCE, ds);
retVal = Persistence.createEntityManagerFactory("application", properties);
}
catch (Exception ex)
{
ex.printStackTrace();
}
return retVal;
}
}
第九步:创建REST服务
要在web.xml配置文件中注册我们的RESTful服务实现,请将我们FavoriteCityService类的完全限定类名添加到逗号分隔列表中jaxrs.serviceClasses。请参阅下面的代码段,了解在元素中输入完全限定类名的位置(不要忘记AuthenticationService行尾的逗号)。
<init-param>
<param-name>jaxrs.serviceClasses</param-name>
<param-value>
com.sap.hana.cloud.samples.weatherapp.api.AuthenticationService,
com.sap.hana.cloud.samples.weatherapp.api.FavoriteCityService
</param-value>
</init-param>
第十步:为json反序列化添加初始化参数。
在上述标记的下方,我们需要为JSON反序列化添加另一个标记,如下所示:
<init-param>
<param-name>jaxrs.providers</param-name>
<param-value>org.codehaus.jackson.jaxrs.JacksonJsonProvider</param-value>
</init-param>
第十一步:定义数据源
要做的最后一个更改是在其中定义一个DataSource web.xml以便连接到底层数据库。为此,请在结束标记之后复制并粘贴以下XML代码段:
<resource-ref>
<res-ref-name>jdbc/DefaultDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
</resource-ref>
第十二步:REST工具
为了正确测试我们的RESTful服务,我们需要一个REST工具(例如Postman),它允许您以方便的方式执行HTTP调用。
在邮递员中,输入http://localhost:8080/weatherapp/api/v1/citiesURL输入字段,并确保Basic Auth在“ 授权”选项卡中提供您的用户名/密码作为参数。
然后,确保按相应的更新请求按钮更新请求。然后,将“Authorization”参数作为HTTP标头参数添加到您的请求中。
执行调用后,您将在成功验证后看到两个空括号“[]”(表示空数组)。别担心,我们还没有将任何城市保存为最爱,所以这正是我们所期望的
至此,我们对于SAP Cloud PlatForm中的使用JPA为应用添加持久层部分已经完成了。