首页 > 代码库 > 完整的MVC框架(前端、后台和数据库)
完整的MVC框架(前端、后台和数据库)
终于学完了数据库的连接,可以做一个完整的项目了,以前做的练习都没有关联到数据库,没法进行事务。
MVC框架
先上图:
老师画的图,有点乱,但是大概意思还是可以理解。
这个练习是简单的存储一个学生读了哪些书,存进数据库中,当然也可以查询。
主页图:
代码奉上:
index.jsp
- <span style="font-size:14px;"><%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
- <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <title>学生图书信息管理</title>
- </head>
- <body>
- <br><a href=http://www.mamicode.com/‘
- <br/><br/>
- <hr/>
- <form action=‘<c:url value="http://www.mamicode.com/StudServlet?cmd=save"></c:url>‘ method="post">
- 姓名<input type="text" name="name" /><br/><br/>
- <fieldset style="width: 200px">
- <legend>图书1</legend>
- 书名<input type="text" name="bookName" />
- 价格<input type="text" name="price" />
- </fieldset>
- <fieldset style="width: 200px">
- <legend>图书2</legend>
- 书名<input type="text" name="bookName" />
- 价格<input type="text" name="price" />
- </fieldset>
- <input type="submit" value="http://www.mamicode.com/保存">
- </form>
- <br/>
- <a href=http://www.mamicode.com/‘
- </body>
- </html>
- </span>
index请求的servlet.java
- <span style="font-size:14px;">package cn.hncu.stud.servlet;
- import java.io.IOException;
- import java.util.List;
- import java.util.Map;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import cn.hncu.domain.Book;
- import cn.hncu.domain.Stud;
- import cn.hncu.stud.service.IStudService;
- import cn.hncu.stud.service.IStudServiceImpl;
- import cn.hncu.utils.TxProxy;
- public class StudServlet extends HttpServlet {
- // IStudService service=new IStudServiceImpl();//这是直接在service层中开启事务,也是version 1
- // IStudService service=TxProxy.getProxy(IStudServiceImpl.class);//version 2
- IStudService service=TxProxy.getProxy(new IStudServiceImpl());//version 3
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doPost(request, response);
- }
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- request.setCharacterEncoding("utf-8");
- String cmd=request.getParameter("cmd");
- if("query".equals(cmd)){//不直接用cmd去判断,是为了防止cmd为null,更安全些
- query(request, response);
- }else if("save".equals(cmd)){
- save(request, response);
- }else if("abc".equals(cmd)){
- abc(request,response);
- }
- }
- private void query(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- List<Map<String,String>> stud=service.query();
- request.setAttribute("stud", stud);
- request.getRequestDispatcher("/jsps/show.jsp").forward(request, response);
- }
- private void save(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- //1.收集 2.组织
- String name=request.getParameter("name");
- //图书信息
- String[] bookNames=request.getParameterValues("bookName");
- String prices[]=request.getParameterValues("price");
- Stud stud=new Stud();
- stud.setName(name);
- if(bookNames==null||bookNames.length==0){//防护一下,防止为空,下面的price也应该做防护的,我省略了
- return;
- }
- for(int i=0;i<bookNames.length;i++){
- Book book=new Book();
- book.setName(bookNames[i]);
- book.setPrice(Double.parseDouble(prices[i]));
- //在这里完成两个值对象的“一对多”关系的数据封装
- stud.getBook().add(book);
- book.setStud(stud);
- }
- //3 调用service层
- try {
- service.save(stud);//把stud放进去就可以
- System.out.println("SUCCESSFULL");
- } catch (Exception e) {
- //导向失败页面
- }
- }
- private void abc(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- service.abc();
- }
- }
- </span>
servlet要组织和封装从前台页面发来的数据,这里用了两个值对象:
Stud.java
- <span style="font-size:14px;">package cn.hncu.domain;
- import java.util.ArrayList;
- import java.util.List;
- /*
- * 一对多中“一方”值对象的建法
- */
- public class Stud {
- private String id;
- private String name;
- //为多方开一个集合,用来体现多表中“一对多”的关系
- private List<Book> books=new ArrayList<Book>();//注意:该集合要在类构造或之前就new出来
- 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 List<Book> getBook() {
- return books;
- }
- public void setBook(List<Book> book) {
- this.books = book;
- }
- @Override
- public String toString() {
- return "Stud [id=" + id + ", name=" + name + ", book=" + books + "]";
- }
- }
- </span>
Book.java
- <span style="font-size:14px;">package cn.hncu.domain;
- /*
- * 一对多中的“多”方值对象的建法
- */
- public class Book {
- private Integer id;//基本数据类型全用包装类来声明,为以后使用框架做技术准备----包装类能够兼容框架(因为一般框架都会使用类反射)
- private String name;
- private Double price;
- //专为“一”方添加一个对象类型的变量(不直接使用String studid)-----体现多表中的“一对多”关系
- private Stud stud=new Stud();//设置书的主人
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public Double getPrice() {
- return price;
- }
- public void setPrice(Double price) {
- this.price = price;
- }
- public Stud getStud() {
- return stud;
- }
- public void setStud(Stud stud) {
- this.stud = stud;
- }
- //多表关联时toString()有一个陷阱:不要出现一方输出另外一方,另外一方又输出这一方的情况,形成无穷循环
- @Override
- public String toString() {
- return "Book [id=" + id + ", name=" + name + ", price=" + price + "]";
- }
- }
- </span>
servlet调用service,service被动态代理的TxProxy.java
- <span style="font-size:14px;">package cn.hncu.utils;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.sql.Connection;
- public class TxProxy implements InvocationHandler {
- private Object srcObj=null;
- private TxProxy(Object srcObj) {
- this.srcObj=srcObj;
- }
- /*version 1
- public static Object getProxy(Object srcObj){
- Object newObj=Proxy.newProxyInstance(
- TxProxy.class.getClassLoader(),
- srcObj.getClass().getInterfaces(),
- new TxProxy(srcObj));
- return newObj;
- }
- */
- /*version 2
- public static<T> T getProxy(Class<T> c){
- Object obj=null;
- try {
- obj = c.newInstance();
- } catch (Exception e) {
- throw new RuntimeException(c+"没有空参方法!");
- }
- Object newObj=Proxy.newProxyInstance(
- TxProxy.class.getClassLoader(),
- obj.getClass().getInterfaces(),
- new TxProxy(obj));
- return (T)newObj;
- }
- */
- public static<T> T getProxy(T srcObj){
- Object newObj=Proxy.newProxyInstance(
- TxProxy.class.getClassLoader(),
- srcObj.getClass().getInterfaces(),
- new TxProxy(srcObj));
- return (T)newObj;
- }
- /*version 1与version 2公用
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- Connection con=null;
- Object returnObj=null;
- try {
- con=ConnUtils5.getConn();
- con.setAutoCommit(false);
- System.out.println("开启事务.........");
- returnObj=method.invoke(srcObj, args);//放行
- con.commit();//
- System.out.println("提交事务.......");
- } catch (Exception e) {
- con.rollback();
- System.out.println("回滚事务.........");
- }finally{
- try {
- con.setAutoCommit(true);
- con.close();
- } catch (Exception e) {
- throw new RuntimeException(e.getMessage(), e);
- }
- }
- return returnObj;
- }
- */
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- if(method.isAnnotationPresent(Transaction.class)){
- System.out.println("此方法存在注解,进行拦截......");
- Connection con=null;
- Object returnObj=null;
- try {
- con=ConnUtils5.getConn();
- con.setAutoCommit(false);
- System.out.println("开启事务.........");
- returnObj=method.invoke(srcObj, args);//放行
- con.commit();//
- System.out.println("提交事务.......");
- } catch (Exception e) {
- con.rollback();
- System.out.println("回滚事务.........");
- }finally{
- try {
- con.setAutoCommit(true);
- con.close();
- } catch (Exception e) {
- throw new RuntimeException(e.getMessage(), e);
- }
- }
- return returnObj;
- }
- System.out.println("没有注解,直接放行....");
- return method.invoke(srcObj, args);
- }
- }
- </span>
service层的接口IStudService.java
- <span style="font-size:14px;">package cn.hncu.stud.service;
- import java.util.List;
- import java.util.Map;
- import cn.hncu.domain.Stud;
- import cn.hncu.utils.Transaction;
- public interface IStudService {
- public abstract List<Map<String,String>> query();
- @Transaction
- public abstract void save(Stud stud) throws Exception ;
- public abstract void abc();
- }
- </span>
接口用到的注解:
Transaction.java
- <span style="font-size:14px;">package cn.hncu.utils;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- @Target(value=http://www.mamicode.com/ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface Transaction {
- }
- </span>
实现类IStudServiceImpl.java
- <span style="font-size:14px;">package cn.hncu.stud.service;
- import java.sql.Connection;
- import java.sql.SQLException;
- import java.util.List;
- import java.util.Map;
- import cn.hncu.domain.Stud;
- import cn.hncu.stud.dao.BookDAO;
- import cn.hncu.stud.dao.BookJdbcDao;
- import cn.hncu.stud.dao.StudDAO;
- import cn.hncu.stud.dao.StudJdbcDao;
- import cn.hncu.utils.ConnUtils5;
- /*
- * 我们以后开发时通常要采用一个dao独立操作一个表,系统中有几个实体表就写几个dao,
- * 框架也是这么做的,架构好。
- *
- * 采用事务的场合:
- * 1.如果只有一个dao,但要执行多条sql语句且涉及增、删、改操作时要开启事务
- * 2.如果一个service调用多个dao,通常也要开启事务
- */
- public class IStudServiceImpl implements IStudService{
- public IStudServiceImpl() {
- }
- StudDAO stuDao=new StudJdbcDao();
- BookDAO booDao=new BookJdbcDao();
- @Override
- public List<Map<String, String>> query() {
- return stuDao.query();
- }
- /*
- * 事务要在service层做,dao层抛异常,在service抓异常,然后就行事务回滚,或者没有异常,提交
- * 但是,service拿到的要和dao拿到的是同一个Connection对象,否则无法进行提交或者回滚
- */
- @Override
- public void save(Stud stud) throws Exception {
- stuDao.save(stud);
- booDao.save(stud.getBook());
- }
- @Override
- public void abc() {
- System.out.println("什么都不做,只是来转转.....");
- }
- }
- </span>
service调用dao层,由于要一个表要对应一个dao,这里有stud表和book表,所以有两个dao
接口StudDAO.java
- <span style="font-size:14px;">package cn.hncu.stud.dao;
- import java.util.List;
- import java.util.Map;
- import cn.hncu.domain.Stud;
- public interface StudDAO {
- public abstract List<Map<String,String>> query();
- public abstract void save(Stud stud) throws Exception;
- }
- </span>
实现类StudJdbcDao.java
- <span style="font-size:14px;">package cn.hncu.stud.dao;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.UUID;
- import cn.hncu.domain.Stud;
- import cn.hncu.utils.ConnUtils5;
- public class StudJdbcDao implements StudDAO{
- @Override
- public List<Map<String, String>> query() {
- List<Map<String, String>> list=new ArrayList<Map<String,String>>();
- //一个map就是一行数据,List<Map<>>就是整个数据表
- Connection con=ConnUtils5.getConn();
- try {
- ResultSet rs=con.createStatement().executeQuery("select * from stud");
- while(rs.next()){
- Map<String, String> map=new HashMap<String, String>();
- map.put("id", rs.getString("id"));
- map.put("name", rs.getString("name"));
- list.add(map);
- }
- rs.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }finally{
- try {
- con.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- return list;
- }
- @Override
- public void save(Stud stud) throws Exception {
- Connection con=ConnUtils5.getConn();
- String id=UUID.randomUUID().toString().replace("-", "");
- String sql="insert into stud(id,name) values(?,?)";
- PreparedStatement pst=con.prepareStatement(sql);
- pst.setString(1, id);
- pst.setString(2, stud.getName());
- pst.executeUpdate();
- // int a=pst.executeUpdate();
- //System.out.println("影响 "+a+" 行");
- stud.setId(id);//为了“多方能够拿到一方的id,在这里补一方的id”
- }
- }
- </span>
接口BookDAO.java
- <span style="font-size:14px;">package cn.hncu.stud.dao;
- import java.util.List;
- import cn.hncu.domain.Book;
- public interface BookDAO {
- public abstract void save(List<Book> list) throws Exception;
- }
- </span>
实现类BookJdbcDao.java
- <span style="font-size:14px;">package cn.hncu.stud.dao;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.SQLException;
- import java.util.List;
- import cn.hncu.domain.Book;
- import cn.hncu.utils.ConnUtils5;
- public class BookJdbcDao implements BookDAO{
- @Override
- public void save(List<Book> list) throws Exception {
- Connection con=ConnUtils5.getConn();
- String sql="insert into book(name,price,studid) values(?,?,?)";
- // INSERT INTO book(NAME,price,studid) VALUES("aa",33.5,"1");
- PreparedStatement pst=con.prepareStatement(sql);
- for(Book book:list){
- pst.setString(1, book.getName());
- pst.setDouble(2, book.getPrice());
- pst.setString(3, book.getStud().getId());
- pst.addBatch();//此处不要参数!!!!!添加到批处理(有很多条sql语句要执行时,用批处理)
- }
- pst.executeBatch();//执行批处理
- }
- }
- </span>
类和包:
效果图:
存储到数据库:
学生表:
图书表:
查询:
在项目中又有两个新的知识点:
ThreadLocal类:
java.lang.ThreadLocal<T>
这个类本质上就是里面维护着一个HashMap<Thread,Object>,key为线程,get方法就是通过Thread.currentThread()判断当前线程,然后在map中找到对应的Object,然后返回出去。
即:同一个线程拿同一个对象,不同线程则是不同对象。
这里做了一些练习:
ThreadLocalUtil.java
- <span style="font-size:14px;">package cn.hncu.threadLocDemo;
- import java.util.Random;
- public class ThreadLocalUtil {
- // private static ThreadLocal<Object> t1=new ThreadLocal<Object>();//线程管理池Map<Thread,Object>
- private static MyThreadLocal<Object> t1=new MyThreadLocal<Object>();//线程管理池Map<Thread,Object>
- public static Object getObj(){
- Object obj=t1.get();//从池中拿一个obj
- if(obj==null){//如果池中没有
- Random r=new Random();
- obj=r.nextInt(500);
- t1.set(obj);//则new obj放进池中去
- }
- return obj;
- }
- }
- </span>
测试类Client.java
- <span style="font-size:14px;">package cn.hncu.threadLocDemo;
- import org.junit.Test;
- public class Client {
- @Test
- public void test1(){
- Object o1=ThreadLocalUtil.getObj();
- Object o2=ThreadLocalUtil.getObj();
- System.out.println("o1: "+o1+",o2: "+o2);
- System.out.println("o1==o2? "+(o1==o2));
- }
- @Test
- public void test2(){
- test1();
- System.out.println();
- Object o3=ThreadLocalUtil.getObj();
- Object o4=ThreadLocalUtil.getObj();
- System.out.println("o3: "+o3+",o4: "+o4);
- System.out.println("o3==o4? "+(o3==o4));
- System.out.println();
- new Thread(){
- @Override
- public void run() {
- Object o4=ThreadLocalUtil.getObj();
- Object o5=ThreadLocalUtil.getObj();
- System.out.println("o4: "+o4+",o5: "+o5);
- System.out.println("o4==o5? "+(o4==o5));
- System.out.println();
- }
- }.start();
- }
- /*
- * 测试结果:obj1,obj2,obj3,obj4由于是同一个线程,所以obj也都相同,
- * obj5和obj6两者相同,但是他们是另外一个线程获取的,所以和obj1`obj4是不相等的
- */
- }
- </span>
测试图:
在知道ThreadLocal的功能后,我们自己模拟了一下它的功能:
MyThreadLocal.java
- <span style="font-size:14px;">package cn.hncu.threadLocDemo;
- import java.util.HashMap;
- import java.util.Map;
- /*
- * 自己模拟ThreadLocal功能,便于理解
- * 虽然表面的功能能够模拟出来,但是ThreadLocal真正的功能远远比这强,比如内存管理
- */
- public class MyThreadLocal<Object> {
- private Map<Thread, Object> map=new HashMap<Thread, Object>();
- public Object get(){
- Thread t=Thread.currentThread();
- return map.get(t);
- }
- public void set(Object obj){
- map.put(Thread.currentThread(), obj);
- }
- }
- </span>
同样可以得出一样的结果,但是仅仅只是模拟了表面功能!
动态代理模板
前面已经贴出代码,但是这里用到了类反射、注解、泛型和动态代理,这几个知识点不是很熟,所以在这里再写遍,提醒一下自己!
TxProxy.java
- <span style="font-size:14px;">package cn.hncu.utils;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.sql.Connection;
- public class TxProxy implements InvocationHandler {
- private Object srcObj=null;
- private TxProxy(Object srcObj) {
- this.srcObj=srcObj;
- }
- public static<T> T getProxy(T srcObj){
- Object newObj=Proxy.newProxyInstance(
- TxProxy.class.getClassLoader(),
- srcObj.getClass().getInterfaces(),
- new TxProxy(srcObj));
- return (T)newObj;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- if(method.isAnnotationPresent(Transaction.class)){
- System.out.println("此方法存在注解,进行拦截......");
- Connection con=null;
- Object returnObj=null;
- try {
- con=ConnUtils5.getConn();
- con.setAutoCommit(false);
- System.out.println("开启事务.........");
- returnObj=method.invoke(srcObj, args);//放行
- con.commit();//
- System.out.println("提交事务.......");
- } catch (Exception e) {
- con.rollback();
- System.out.println("回滚事务.........");
- }finally{
- try {
- con.setAutoCommit(true);
- con.close();
- } catch (Exception e) {
- throw new RuntimeException(e.getMessage(), e);
- }
- }
- return returnObj;
- }
- System.out.println("没有注解,直接放行....");
- return method.invoke(srcObj, args);
- }
- }
- </span>
完整的MVC框架(前端、后台和数据库)