如何在本地servlet中使用Google App Engine的Objectify实现数据增删改
GAE Servlet + JSON场景Objectify实现学生数据CRUD示例
前置配置
1. 引入依赖
在pom.xml中添加相关依赖:
<!-- Objectify 依赖 --> <dependency> <groupId>com.googlecode.objectify</groupId> <artifactId>objectify</artifactId> <version>6.1.1</version> </dependency> <!-- Servlet API 依赖 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!-- Jackson JSON 处理依赖 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency>
2. 配置web.xml
添加Objectify过滤器保证请求会话正常,同时配置初始化监听器注册实体:
<!-- Objectify 过滤器 --> <filter> <filter-name>ObjectifyFilter</filter-name> <filter-class>com.googlecode.objectify.ObjectifyFilter</filter-class> </filter> <filter-mapping> <filter-name>ObjectifyFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 实体注册监听器 --> <listener> <listener-class>com.example.ObjectifyInitListener</listener-class> </listener> <!-- Servlet 配置 --> <servlet> <servlet-name>StudentServlet</servlet-name> <servlet-class>com.example.StudentServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>StudentServlet</servlet-name> <url-pattern>/Servlet</url-pattern> </servlet-mapping>
3. 编写实体类和初始化监听器
Student实体类(替换原有Database类)
package com.example; import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Id; @Entity public class Student { @Id private Long id; private String first_name; private String last_name; private String fullname; private String email; private String address; private String contact; // 必须保留无参构造函数 public Student() {} // 所有字段的getter和setter public Long getId() {return id;} public void setId(Long id) {this.id = id;} public String getFirst_name() {return first_name;} public void setFirst_name(String first_name) {this.first_name = first_name;} public String getLast_name() {return last_name;} public void setLast_name(String last_name) {this.last_name = last_name;} public String getFullname() {return fullname;} public void setFullname(String fullname) {this.fullname = fullname;} public String getEmail() {return email;} public void setEmail(String email) {this.email = email;} public String getAddress() {return address;} public void setAddress(String address) {this.address = address;} public String getContact() {return contact;} public void setContact(String contact) {this.contact = contact;} }
Objectify初始化监听器
package com.example; import com.googlecode.objectify.ObjectifyService; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class ObjectifyInitListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { // 注册实体类,必须在所有Objectify操作前执行 ObjectifyService.register(Student.class); } @Override public void contextDestroyed(ServletContextEvent sce) {} }
重构后的Servlet代码
package com.example; import com.fasterxml.jackson.databind.ObjectMapper; import static com.googlecode.objectify.ObjectifyService.ofy; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; public class StudentServlet extends HttpServlet { private ObjectMapper mapper = new ObjectMapper(); @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); String action = request.getParameter("action"); // 查询所有学生数据 if ("list".equals(action)) { List<Student> students = ofy().load().type(Student.class).list(); mapper.writeValue(response.getOutputStream(), students); return; } // 读取请求体JSON BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream())); StringBuilder jsonBuilder = new StringBuilder(); String line; while ((line = br.readLine()) != null) { jsonBuilder.append(line); } String json = jsonBuilder.toString(); // 新增学生 if ("add".equals(action)) { Student student = mapper.readValue(json, Student.class); ofy().save().entity(student).now(); } // 编辑学生 else if ("edit".equals(action)) { Student updateStudent = mapper.readValue(json, Student.class); Student existStudent = ofy().load().type(Student.class).id(updateStudent.getId()).now(); if (existStudent != null) { existStudent.setFirst_name(updateStudent.getFirst_name()); existStudent.setLast_name(updateStudent.getLast_name()); existStudent.setFullname(updateStudent.getFullname()); existStudent.setEmail(updateStudent.getEmail()); existStudent.setAddress(updateStudent.getAddress()); existStudent.setContact(updateStudent.getContact()); ofy().save().entity(existStudent).now(); } } // 删除学生 else if ("delete".equals(action)) { Student deleteStudent = mapper.readValue(json, Student.class); ofy().delete().type(Student.class).id(deleteStudent.getId()).now(); } // 返回最新的学生列表 List<Student> students = ofy().load().type(Student.class).list(); mapper.writeValue(response.getOutputStream(), students); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
调整后的myFunction.js代码
// 全局变量保存当前编辑的学生ID let editId = null; // 页面加载完成后绑定事件 $(document).ready(function () { // 页面加载时先拉取所有学生数据 loadStudentList(); // 注册/更新按钮点击事件 $("#bttHello").click(function () { let studentData = {}; studentData.id = editId; studentData.fullname = $("#fullname").val(); studentData.first_name = $('#first_name').val(); studentData.last_name = $('#last_name').val(); studentData.email = $('#email').val(); studentData.address = $('#address').val(); studentData.contact = $('#contact').val(); let action = editId ? 'edit' : 'add'; $.ajax({ url: 'Servlet?action=' + action, type: 'POST', dataType: 'json', data: JSON.stringify(studentData), contentType: 'application/json', success: function (result) { renderStudentTable(result); // 清空表单 $('#first_name, #last_name, #fullname, #email, #address, #contact').val(''); editId = null; $("#bttHello").val('Register'); }, error: function (data, status, er) { alert("操作失败: " + er); } }); }); }); // 加载学生列表 function loadStudentList() { $.ajax({ url: 'Servlet?action=list', type: 'GET', dataType: 'json', success: function (result) { renderStudentTable(result); } }); } // 渲染学生表格 function renderStudentTable(students) { $("tr:has(td)").remove(); $.each(students, function (index, student) { $("#added-data").append($('<tr/>') .append($('<td/>').html(student.first_name)) .append($('<td/>').html(student.last_name)) .append($('<td/>').html(student.fullname)) .append($('<td/>').html(student.email)) .append($('<td/>').html(student.address)) .append($('<td/>').html(student.contact)) .append($('<td/>').html("<button type='button' class='btn btn-default btn-sm edit-btn' data-id='" + student.id + "'>" + "<span class= 'glyphicon glyphicon-pencil' ></span> 编辑</button>")) .append($('<td/>').html("<button type='button' class='btn btn-default btn-sm delete-btn' data-id='" + student.id + "'>" + "<span class='glyphicon glyphicon-remove'></span> 删除</button>")) ); }); // 绑定编辑按钮事件 $(".edit-btn").click(function () { editId = $(this).data('id'); let row = $(this).parent().parent(); let cells = row.find('td'); $('#first_name').val(cells.eq(0).text()); $('#last_name').val(cells.eq(1).text()); $('#fullname').val(cells.eq(2).text()); $('#email').val(cells.eq(3).text()); $('#address').val(cells.eq(4).text()); $('#contact').val(cells.eq(5).text()); $("#bttHello").val('更新'); }); // 绑定删除按钮事件 $(".delete-btn").click(function () { if (confirm('确认删除该学生数据?')) { let deleteId = $(this).data('id'); $.ajax({ url: 'Servlet?action=delete', type: 'POST', dataType: 'json', data: JSON.stringify({id: deleteId}), contentType: 'application/json', success: function (result) { renderStudentTable(result); } }); } }); }
本地运行注意点
- 启动GAE本地开发服务器后,所有数据会持久化保存在本地Datastore文件中,重启服务不会丢失数据
- 所有Objectify的数据库操作必须在被ObjectifyFilter拦截的请求中执行,否则会抛出无会话异常
- 实体类必须保留无参构造函数,否则Objectify无法反序列化数据
内容的提问来源于stack exchange,提问作者Beginner
相关产品推荐
相关产品推荐

