JavaEE 7教程
AngularJS +Java EE 7教程源码案例
这是一个简单的分页列表案例,提供REST服务。技术栈如下:
该案例源码下载地址:Github
后端:JavaEE7
实体模型代码如下:
@Entity
public class Person {
@Id
private Long id;
private String name;
private String description;
}
@Entity是JPA元注解,进行持久化数据库使用。持久化配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="myPU" transaction-type="JTA">
<properties>
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
<property name="javax.persistence.schema-generation.create-source" value="script"/>
<property name="javax.persistence.schema-generation.drop-source" value="script"/>
<property name="javax.persistence.schema-generation.create-script-source" value="sql/create.sql"/>
<property name="javax.persistence.schema-generation.drop-script-source" value="sql/drop.sql"/>
<property name="javax.persistence.sql-load-script-source" value="sql/load.sql"/>
</properties>
</persistence-unit>
</persistence>
Java EE 7有两个新特性: 使用属性javax.persistence.schema-generation.*你可以标准方式运行SQL,如果你没有提供数据库源,它会绑定缺省的数据库。在这个案例中,我们使用内部Wildfly H2 数据库配置
下面是提供REST服务代码:
@Stateless
@ApplicationPath("/resources")
@Path("persons")
public class PersonResource extends Application {
@PersistenceContext
private EntityManager entityManager;
private Integer countPersons() {
Query query = entityManager.createQuery("SELECT COUNT(p.id) FROM Person p");
return ((Long) query.getSingleResult()).intValue();
}
@SuppressWarnings("unchecked")
private List<Person> findPersons(int startPosition, int maxResults, String sortFields, String sortDirections) {
Query query = entityManager.createQuery("SELECT p FROM Person p ORDER BY " + sortFields + " " + sortDirections);
query.setFirstResult(startPosition);
query.setMaxResults(maxResults);
return query.getResultList();
}
public PaginatedListWrapper<Person> findPersons(PaginatedListWrapper<Person> wrapper) {
wrapper.setTotalResults(countPersons());
int start = (wrapper.getCurrentPage() - 1) * wrapper.getPageSize();
wrapper.setList(findPersons(start,
wrapper.getPageSize(),
wrapper.getSortFields(),
wrapper.getSortDirections()));
return wrapper;
}
@GET
@Produces(MediaType.APPLICATION_JSON)
public PaginatedListWrapper<Person> listPersons(@DefaultValue("1")
@QueryParam("page")
Integer page,
@DefaultValue("id")
@QueryParam("sortFields")
String sortFields,
@DefaultValue("asc")
@QueryParam("sortDirections")
String sortDirections) {
PaginatedListWrapper<Person> paginatedListWrapper = new PaginatedListWrapper<>();
paginatedListWrapper.setCurrentPage(page);
paginatedListWrapper.setSortFields(sortFields);
paginatedListWrapper.setSortDirections(sortDirections);
paginatedListWrapper.setPageSize(5);
return findPersons(paginatedListWrapper);
}
}
这里使用普通Java对象POJO, 使用了 Java EE元注解增强其行为。 @ApplicationPath("/resources") 和 @Path("persons") 暴露提供REST服务,其URL是yourdomain/resources/persons, @GET 对应http GET 方法调用,@Produces(MediaType.APPLICATION_JSON) 是格式化REST输出为JSON格式.
下面我们需要一个类提供批量分页查询:
public class PaginatedListWrapper<T> {
private Integer currentPage;
private Integer pageSize;
private Integer totalResults;
private String sortFields;
private String sortDirections;
private List<T> list;
}
UI前端AngularJS
Angular扩展了传统HTML,使用了附加的标签属性,其和模型数据绑定,以一种MVC 方式,看看我们的Html:
<!DOCTYPE html>
<!-- Declares the root element that allows behaviour to be modified through Angular custom HTML tags. -->
<html ng-app="persons">
<head>
<title></title>
<script src="lib/angular.min.js"></script>
<script src="lib/jquery-1.9.1.js"></script>
<script src="lib/ui-bootstrap-0.10.0.min.js"></script>
<script src="lib/ng-grid.min.js"></script>
<script src="script/person.js"></script>
<link rel="stylesheet" type="text/css" href="lib/bootstrap.min.css"/>
<link rel="stylesheet" type="text/css" href="lib/ng-grid.min.css"/>
<link rel="stylesheet" type="text/css" href="css/style.css"/>
</head>
<body>
<br>
<div class="grid">
<!-- Specify a JavaScript controller script that binds Javascript variables to the HTML.-->
<div ng-controller="personsList">
<!-- Binds the grid component to be displayed. -->
<div class="gridStyle" ng-grid="gridOptions"></div>
<!-- Bind the pagination component to be displayed. -->
<pagination direction-links="true" boundary-links="true"
total-items="persons.totalResults" page="persons.currentPage" items-per-page="persons.pageSize"
on-select-page="refreshGrid(page)">
</pagination>
</div>
</div>
</body>
</html>
因为Javascript 和CSS是分离的,这里只需要少量语法, Angular能够使用很多组件,这里是ng-grid显示数据,而 UI Bootstrap提供分页组件 当然ng-grid也有分页功能。
person.js是控制器:
var app = angular.module('persons', ['ngGrid', 'ui.bootstrap']);
// Create a controller with name personsList to bind to the html page.
app.controller('personsList', function ($scope, $http) {
// Makes the REST request to get the data to populate the grid.
$scope.refreshGrid = function (page) {
$http({
url: 'resources/persons',
method: 'GET',
params: {
page: page,
sortFields: $scope.sortInfo.fields[0],
sortDirections: $scope.sortInfo.directions[0]
}
}).success(function (data) {
$scope.persons = data;
});
};
// Do something when the grid is sorted.排序
// The grid throws the ngGridEventSorted that gets picked up here and assigns the sortInfo to the scope.
// This will allow to watch the sortInfo in the scope for changed and refresh the grid.
$scope.$on('ngGridEventSorted', function (event, sortInfo) {
$scope.sortInfo = sortInfo;
});
// Watch the sortInfo variable. If changes are detected than we need to refresh the grid.
// This also works for the first page access, since we assign the initial sorting in the initialize section.
$scope.$watch('sortInfo', function () {
$scope.refreshGrid($scope.persons.currentPage);
}, true);
// Initialize required information: sorting, the first page to show and the grid options.
$scope.sortInfo = {fields: ['id'], directions: ['asc']};
$scope.persons = {currentPage : 1};
$scope.gridOptions = {
data: 'persons.list',
useExternalSorting: true,
sortInfo: $scope.sortInfo
};
});
运行效果如下: