JAVA WEB---事務+ThreadLocal+添加商品

1.事務


  1.我們可以具體分析下隔離性產生的細節:
    如果兩個線程併發修改,必然產生多線程併發安全問題,必須隔離開
    如果兩個線程併發查詢,必然沒有問題,不需要隔離
    如果一個線程修改,一個線程查詢,在不同的應用場景下有可能有問題,有可能沒問題。安全性要求高->互斥,安全性要求不高->不互斥
    
    2.爲了實現上面描述的情況,數據庫設計者提供了兩種鎖
        2.1共享鎖          共享鎖和共享鎖可以共存,共享鎖和排他鎖不能共存
        2.2排它鎖          排他鎖和共享鎖不能共存,排他鎖和排他鎖也不能共存
        2.3可能的死鎖       當兩邊都是Serializable隔離級別時
                                     兩邊都先進行查詢 再嘗試進行修改 則互相等待對方釋放共享鎖 都無法接着執行 造成了死鎖
                                     死鎖的解決有兩種辦法:避免死鎖 解決死鎖
                                  mysql沒有避免死鎖 嘗試檢測死鎖 發現死鎖後,如果有一方已經阻塞,而另一方的操作會導致死鎖,另一方rollback
        
    3.鎖與數據庫操作的關係
        3.1非Serializable隔離級別下做查詢不加任何鎖
        3.2在Serializable隔離級別下做查詢加共享鎖
        3.3任何級別下,增刪改查都加排他鎖
        
        注意:不加鎖的操作和任何鎖都不互斥。
        
     4. 
        a(非Serializable) b(非Serializable)          互斥             原因
             查詢                            查詢              不互斥        a:不加鎖 b:不加鎖
             修改                            查詢                不互斥        a:排他鎖 b:不加鎖
             查詢                            修改              不互斥        a:不加鎖 b:排他鎖
             修改                            修改               互斥        a:排他鎖 b:排他鎖
            
                       注意:兩個都不是Serializable,互斥一個
                                兩個都不是Serializable,僅在兩個都修改的時候互斥。
                       
        a(Serializable)  b(非Serializable)            互斥             原因
             查詢                            查詢              不互斥        a:共享鎖 b:不加鎖
             修改                            查詢                不互斥        a:排他鎖 b:不加鎖
             查詢                            修改                互斥        a:共享鎖 b:排他鎖
             修改                            修改                互斥        a:排他鎖 b:排他鎖
            
                       注意:兩個一個是Serializable,一個不是Serializable,互斥兩個
                                兩個一個是Serializable,一個不是Serializable, 當兩個都在修改和 Serializable查詢,不是Serializable修改的時候互斥
             
        a(Serializable)  b(Serializable)             互斥                      原因
             查詢                            查詢              不互斥        a:共享鎖 b:共享鎖
             修改                            查詢                 互斥        a:排他鎖 b:共享鎖
             查詢                            修改               互斥        a:共享鎖 b:排他鎖
             修改                            修改               互斥        a:排他鎖 b:排他鎖
            
                       注意:兩個都是Serializable,互斥三個     
                    兩個都是Serializable,僅在兩個查詢的時候不互斥。
        
        

 

2.ThreadLocal

package com.hxuner.thread;

//模擬連接對象
public class Myconn {

}
package com.hxuner.thread;

public class MyRunn1 implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		ThreadLocal01.getInstance().startTransaction();  //當前線程開啓事務
		try {
			Thread.sleep(2000);               			//線程阻塞2秒,模擬進行其他操作
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		ThreadLocal01.getInstance().commit();			//當前線程提交事務
	}

	
	
}
package com.hxuner.thread;

import java.sql.Connection;
import java.sql.SQLException;





public class ThreadLocal01 {
	    //單例
		//1.私有的構造器
		private ThreadLocal01(){}
		
		//2.私有的靜態的本類唯一實例
		
		private static ThreadLocal01 tm=new ThreadLocal01();
		
		//3.公有的靜態的返回本類唯一實例的方法
		public static ThreadLocal01 getInstance(){
			return tm;
		}
		
		//唯一的連接對象conn
		private Myconn conn;	//多個線程操作會出現問題 , 
								//第一個線程調用這個方法,獲取連接對象@123
								//第二個線程也調用這個方法,獲取連接對象@789
								//由於只有一個,整個conn都變爲@789,而線程1不知道,線程1和線程2提交的都是@789,出現併發問題
		/*
			出現問題的原因:
						單例模式+唯一的實例, 由於是唯一的引用,多線程進來會改
			解決:
						ThreadLocal:利用線程本地變量來解決
		*/
		/**
		 * 開啓事務startTransaction()
		 */
		public void startTransaction(){
			conn=new Myconn();	//開啓事務的時候獲取唯一的連接池對象
			System.out.println("線程"+Thread.currentThread().getName()+"開啓事務,使用的連接對象是"+conn);
		}
		
		/**
		 * 提交事務commit()
		 */
		public void commit() {
			System.out.println("線程"+Thread.currentThread().getName()+"提交了事務,使用的連接對象是"+conn);
		}
		
}
package com.hxuner.thread;

public class zTest {
	public static void main(String[] args) {
		
		/*
		只需要Myconn()連接對象,MyRunn1線程,ThreadLocal01的conn管理類(唯一的連接對象conn private Myconn conn;)
		
		Thread t1=new Thread(new MyRunn1());
		Thread t2=new Thread(new MyRunn1());
		 t1.start();
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		 t2.start();
		 
		 輸出:
		 線程Thread-0開啓事務,使用的連接對象是com.hxuner.thread.Myconn@55f33675
		 線程Thread-1開啓事務,使用的連接對象是com.hxuner.thread.Myconn@525483cd
		 線程Thread-0提交了事務,使用的連接對象是com.hxuner.thread.Myconn@525483cd
		 線程Thread-1提交了事務,使用的連接對象是com.hxuner.thread.Myconn@525483cd
		 
		 原因:
		 由於只有一個,整個conn都變爲@525483cd,而線程1不知道,線程1和線程2提交的都是@525483cd,出現併發問題
		 */
    }
}

 

----------------------------------------------------ThreadLocal------------------------------------------------------------------------------

package com.hxuner.thread;

//模擬連接對象
public class Myconn {

}
package com.hxuner.thread;

public class MyRunn2 implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		ThreadLocal02.getInstance().startTransaction();  //當前線程開啓事務
		try {
			Thread.sleep(2000);               			//線程阻塞2秒,模擬進行其他操作
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		ThreadLocal02.getInstance().commit();			//當前線程提交事務
	}

	
	
}
package com.hxuner.thread;

import java.sql.Connection;
import java.sql.SQLException;

/*
 * 	1.ThreadLocal:線程本地變量
	 	1.1Thread類中有一個map集合,是一個實例成員
	 	1.2每一個線程對象有一個屬於自己的map集合,每個線程如果希望使用自己的變量,可以將變量存入自己的那個map集合中。
 	
 	2.線程併發問題:存在於多個線程併發操作同一個變量(實例成員)
 	    解決:
 		通過線程本地變量,每個線程操作的是保存在自身的map集合中的變量
 		所以多線程併發操作的不再是同一個變量,進而解決了線程安全問題。
 	  
 	  但是!!!,線程Thread自身的那個map集合,不能通過線程對象直接訪問。
 	  只能通過Java提供的一個工具類來訪問:ThreadLocal
 	  
 	  
 	 3.ThreadLocal
 	 	3.1概念
---------------------線程對象有一個自己的Map集合ThreadLocalMap,Map集合ThreadLocalMap存放{ThreadLocal,value}鍵值對----------------------------------------
 	 		
 	 		ThreadLocal是用來操作每個線程對象自己的那個Map集合的工具類
 	 		每個線程對象,都有一個實例成員  ThreadLocalMap
 	 		ThreadLocalMap,是一個特殊的Map集合,專門用於保存線程的本地變量,該集合不能通過線程對象來直接訪問,只能通過ThreadLocal來訪問
 	 	
 	 	3.2API
 	 		3.1	ThreadLocal<T> tl=new ThreadLocal(); 用於訪問每個線程對象自己的那個map集合的工具類
 	 		 									        T代表存入map集合的value的類型
 	 		 

            								   
 	 		 3.2 
 	 		 tl.set(obj); --> 獲取當前線程 得到當前線程內部的map 向map中存儲(tl,obj)的鍵值對
			 set(obj){
				Thread t=Thread.currentThread();		//1.獲取當前線程對象
			  	ThreadLocalMap map = getMap(t);			//2.獲取當前線程對象身上的threadLocalMap->map集合    1.如有,則直接拿來使用 2.如爲null,則爲其創建一個map集合
			  	map.set(this, value);				    //3.將tl作爲key,將傳入的參數作爲值,存入map集合中
			  }
 	  
 	  
 	  		3.3
 	  		tl.get();    --> 獲取當前線程 得到當前線程內部的map 從map中查找tl對應的值 返回
 	  		get(){
				 Thread t = Thread.currentThread();		      //1.獲取當前線程對象
        		 ThreadLocalMap map = getMap(t);			  //2.獲取當前線程對象身上的threadLocalMap->map集合
        		 map.getEntry(this); 						  //3.使用自身(tl對象)作爲key,從map集合中查詢對應的value
			 }
			 
			 
			因此,對於每個線程來說,每個tl對象,僅可以存1個值
			但是這個tl對象,可以在多個線程間公用
			如果一個線程要存儲多個本地變量,則需要創建多個tl對象
 	
 */









public class ThreadLocal02 {
	//單例
		//1.私有的構造器
		private ThreadLocal02(){}
		
		//2.私有的靜態的本類唯一實例
		
		private static ThreadLocal02 tm=new ThreadLocal02();
		//3.公有的靜態的返回本類唯一實例的方法
		public static ThreadLocal02 getInstance(){
			return tm;
		}
		
		//private Myconn conn;//多個線程操作會出現問題
		
	
		private ThreadLocal<Myconn> t1=new ThreadLocal<Myconn>();//	ThreadLocal,用於訪問每個線程對象自己的那個map集合ThreadLocalMap的工具類
																 // 泛型是需要保存在每個線程自身的map集合中的value的類型
																 // 每個ThreadLocal對應Thread的map集合中的一個key
		
	
		
		/**
		 * 開啓事務startTransaction()
		 */
		public void startTransaction(){
			//創建當前線程使用的連接對象
			Myconn conn=new Myconn();	//開啓事務的時候獲取唯一的連接池對象
			
			//Thread t=Thread.currentThread();  
			//t.getMap()  	Thread對象內置了一個Map來存取消息,但是這個map外界無法直接操作
			//需要通過ThreadLocal來實現對Thread中的Map進行數據的存取
			
			//將當前線程使用的連接對象存入線程自身的map對象
			t1.set(conn);   //key=ThreadLocal當前線程,value=conn
			
			System.out.println("線程"+Thread.currentThread().getName()+"開啓事務操作的連接對象conn是"+conn);
		}
		
		
		
		/**
		 * 提交事務commit()
		 */
		public void commit() {
			//從線程自己的map集合中獲取之前使用的conn對象
			Myconn conn=t1.get();
			System.out.println("線程"+Thread.currentThread().getName()+"提交事務操作的連接對象conn是"+conn);
		}
		
}
package com.hxuner.thread;

public class zTest {
	public static void main(String[] args) {
		
		
		//只需要Myconn()連接對象,MyRunn2線程,ThreadLocal02的conn管理類(private ThreadLocal<Myconn> t1=new ThreadLocal<Myconn>();每個ThreadLocal對應Thread的map集合中的一個key)
		Thread t1=new Thread(new MyRunn2());
		Thread t2=new Thread(new MyRunn2());
		 t1.start();
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		 t2.start();
		/*
		 * 輸出	
		  		線程Thread-0開啓事務操作的連接對象conn是com.hxuner.thread.Myconn@55f33675
				線程Thread-1開啓事務操作的連接對象conn是com.hxuner.thread.Myconn@525483cd
				線程Thread-0提交事務操作的連接對象conn是com.hxuner.thread.Myconn@55f33675
				線程Thread-1提交事務操作的連接對象conn是com.hxuner.thread.Myconn@525483cd
		
		  通過線程本地變量,每個線程操作的是保存在自身的map集合中的變量
 		  所以多線程併發操作的不再是同一個變量,進而解決了線程安全問題。
		 */
	}
}

---------------------------------EasyMall-----------------------------事務耦合類------------------------------------------------

package com.hxuner.util;

import java.sql.Connection;
import java.sql.SQLException;
/*
   EasyMall商品模塊實現 - 添加商品 - 事務控制
   
   1.事務的引出:
 			在ProdServiceImpl插入商品和插入商品種類表之間出現異常int i=10/0;數據庫中插入了商品種類表,但是對應的商品未插入
			所以應該是事務級別,要麼都完成,要麼都不完成
	
	2.事務開啓解決方案
			ProdServiceImpl中 addProd()增加商品方法中
      ------------------注意: 在Service中需要操作事務,需要在Service中使用Conn對象,造成和Dao強耦合,引出TransactionManager--------
				try{
					開啓事務 conn.setAutoCommit(false);
						一系列操作{
							使用cname查prob_category表,查看是否有數據  getPCByCname()
							無數據,插入商品種類表					 insertProdcategory()
							查詢商品種類表PC							 getPCByCname()
							插入商品									 insertProd()
							
						}
					
					提交事務 conn.commit()
					
				}catch(){
					回滾事務 conn.rollback()
				}
				
			ProdDaoImpl中
				getPCByCname{
					conn=JDBCUtils.getConn();
	-----------------注意:將conn對象返回給ProdServiceImpl,這樣ProdServiceImpl才能實現事務----------------------
				}
	
	 3.TransactionManager的引出
		       在Service中需要操作事務,將一個方法中的多個對數據庫的操作放到同一個事務下進行
		       如果在Service中使用Conn對象,會造成Service和Dao層的強耦合
		       這種耦合不可避免,因此嘗試使用一個工具類將耦合管理起來,這個工具類就是TransactionManager
		
	
			開發了TransactionManager 在其中管理Connection 
		   	對外提供 1.startTransaction()開啓事務  2.commit()提交事務 3.rollback()回滾事務 4.getConn()獲取連接對象  5.closeConn()關閉連接對象
		       之後所有和事務 相關的操作都不要直接使用Conn 而是通過TransactionManager來實現管理
		      
		      
			解決耦合性的問題 - 本質上是將耦合轉移到了TransactionManager中同一管理
			雖然沒有徹底的解決耦合 但是統一管理起來 方便未來開發和維護

 */
/*
  TransactionManager
	  	  問題1:設計單例還是多例
	  			如果是多例,那麼需要在Service中創建實例new Connection(),而Dao中應該使用同一個實例,這種情況下需要service將conn傳入給Dao,再次造成強耦合。
	  			所以設計爲單例,或者做成靜態工具類(內部的成員變量都是靜態的)
	 			
	           問題2:多線程下的線程安全問題
	            解決方案:	
	                      方案1:在類的內部通過靜態Connection加鎖的方式來管理 - 可以解決問題,但是所有事務排隊,效率非常低
				方案2:通過ThreadLocal編碼,實現所有的線程都各自攜帶各自的Connection對象,從而讓管理各自的事務 - 不會有阻塞 效率高
 */
public class TransactionManager {
	//單例
	//1.私有的構造器
	private TransactionManager(){}
	
	//2.私有的靜態的本類唯一實例
	
	private static TransactionManager tm=new TransactionManager();
	
	//3.公有的靜態的返回本類唯一實例的方法
	public static TransactionManager getInstance(){
		return tm;
	}
	
	//private Connection conn;  //多個線程操作會出現問題  
								//第一個線程調用這個方法,獲取連接對象@123
								//第二個線程也調用這個方法,獲取連接對象@789
								//由於只有一個,整個conn都變爲@789,而線程1不知道,線程1和線程2提交的都是@789,出現併發問題
	
	//ThreadLocal
  //---------------------線程對象有一個自己的Map集合ThreadLocalMap,Map集合ThreadLocalMap存放{ThreadLocal,value}鍵值對-------------------------
	private ThreadLocal<Connection> tl=new  ThreadLocal<Connection>(); //ThreadLocal是操作線程用來保存本地變量的map集合的工具
																	   //每個ThreadLocal對應Thread的map集合ThreadLocalMap中的一個key
	
	
	/**
	 * 開啓事務startTransaction()
	 */
	public void startTransaction(){
		Connection conn=JDBCUtils.getConn();	//每個線程獲取屬於自己的開啓事務的時候獲取唯一的連接池對象
		try {
			conn.setAutoCommit(false);			//基於該連接對象開啓事務
			tl.set(conn);						//將conn保存到當前線程的map集合中,供該線程後續使用
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 提交事務commit()
	 */
	public void commit() {
		Connection conn=tl.get();		 //從當前線程對象的map集合中獲取之前保存的連接對象
		if(conn!=null){					
			try {
				conn.commit();			//使用該連接對象提交事務
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 回滾事務rollback()
	 */
	public void rollback(){
		Connection conn=tl.get();	 //從當前線程對象的map集合中獲取之前保存的連接對象
		if(conn!=null){
			try {
			conn.rollback();		 //基於該連接對象回滾事務
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 獲取唯一實例連接對象Conn	getConn()
	 */
	public Connection getConn(){
		return tl.get();		//返回當前線程對象保存的conn對象
	}	
	
	/**
	 * 關閉連接對象 close()
	 */
	public void closeConn(){
		Connection conn=tl.get();	//從當前線程對象的map集合中獲取之前保存的連接對象
		if(conn!=null){
			try {
				conn.close();		//基於該連接對象關閉連接
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
}

3.添加商品

3.1ProdListServlet

package com.hxuner.web;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.hxuner.domain.Prod;
import com.hxuner.factory.BaseFactory;
import com.hxuner.service.ProdService;
/*
 * 顯示商品邏輯:
 * 		用戶請求->ProdListServlet->ProdServiceImpl->ProdDaoImpl->返回給ProdListServlet->prodList.jsp
 * 		ProdListServlet
 * 						//1.接受請求參數 無
 * 						//2.表單驗證
 * 						  3.調用service查詢所有產品的數據
 * 						  4.將獲取到的數據存入request作用域
 * 						  5.轉發給ProdList.jsp
 *      
 *      ProdService    
 *      			List<Prod>  listProd(){}查詢所有產品的數據
 *      
 *      ProdDao	
 *      			List<Prod>	listProd(){}查詢所有產品的數據
 *      
 *      
 *      ProdList.jsp
 *      			
 *      			從request作用域獲取數據並展示
 */

/*
 * ProdListServlet
 * 						//1.接受請求參數 無
 * 						//2.表單驗證
 * 						  3.調用service查詢所有產品的數據
 * 						  4.將獲取到的數據存入request作用域
 * 						  5.轉發給ProdList.jsp
 */
public class ProdListServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//調用service對應的方法listAllProd();,獲取所有商品數據
		ProdService service=BaseFactory.getFactory().getInstance(ProdService.class);
		List<Prod> list=service.listAllProd();
		
		//將商品存入request作用域
		request.setAttribute("prods", list);
		
		// 將請求轉發給prodList.jsp
		request.getRequestDispatcher("/prodList.jsp").forward(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

}

3.2ProdService

/**
	 * 查詢全部商品信息的方法
	 * @return 封裝了商品數據的javaBean的集合或者是null
	 */
	List<Prod> listAllProd();

ProdServiceImpl

package com.hxuner.service;

import java.sql.Connection;
import java.util.List;

import com.hxuner.dao.ProdDao;
import com.hxuner.domain.Prod;
import com.hxuner.domain.ProdCategory;
import com.hxuner.exception.MsgException;
import com.hxuner.factory.BaseFactory;
import com.hxuner.util.TransactionManager;
/*
 	1.事務的引出:
 			在插入商品和插入商品種類表之間出現異常int i=10/0;數據庫中插入了商品種類表,但是對應的商品未插入
			所以應該是事務級別,要麼都完成,要麼都不完成
	
	2.事務開啓解決方案
			ProdServiceImpl中 addProd()增加商品方法中
				try{
					開啓事務 conn.setAutoCommit(false);
						一系列操作{
							使用cname查prob_category表,查看是否有數據  getPCByCname()
							無數據,插入商品種類表					 insertProdcategory()
							查詢商品種類表PC							 getPCByCname()
							插入商品									 insertProd()
							
						}
					
					提交事務 conn.commit()
					
				}catch(){
					回滾事務 conn.rollback()
				}
				
			ProdDaoImpl中
				getPCByCname{
					conn=JDBCUtils.getConn();
	-----------------注意:將conn對象返回給ProdServiceImpl,這樣ProdServiceImpl才能實現事務----------------------
				}
	
	 3.TransactionManager的引出
		    在Service中需要操作事務,將一個方法中的多個對數據庫的操作放到同一個事務下進行
		    如果在Service中使用Conn對象,會造成Service和Dao層的強耦合
		    這種耦合不可避免,因此嘗試使用一個工具類將耦合管理起來,這個工具類就是TransactionManager
		
			
 
 */
public class ProdServiceImpl implements ProdService {
	private ProdDao prodDao=BaseFactory.getFactory().getInstance(ProdDao.class);
	
	//prod缺少了cid,表單提交只有cname,根據cname獲取cid,寫入prod使其完整。再插入數據庫
	@Override
	public boolean addProd(Prod prod) {
		//1.先使用cname查prob_category表,查看是否有數據
		//	1.1有數據,返回id
		//	1.2沒有數據,先在prob_categroy表中添加一行數據
		//		再進行一次查詢獲取cid
		//2.使用cid給prob賦值
		//3.添加商品信息到prob表
		
		boolean flag1=false;//是否增加成功,即返回值
		//1.先使用cname查prob_category表,查看是否有數據
		
		   try{
			   ProdCategory pc=null;
			   //--------開啓事務----------------------
			   TransactionManager.getInstance().startTransaction();
				//1.1有數據,返回id
				try {
					
					pc=prodDao.getPCByCname(prod.getCname());
				} catch (MsgException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				}
				
				//1.2沒有數據,先在prob_categroy表中添加一行數據
				if(pc==null){
					//創建一個ProdCategory的對象,封裝想數據庫中插入的信息。
					ProdCategory pc2=new ProdCategory(-1,prod.getCname());
					boolean flag=prodDao.insertProdCategory(pc2);
					if(!flag){
						//商品種類添加失敗,則商品也無法繼續添加
						return false;
					}
					
					
					//再進行一次查詢獲取cid
					try {
						pc=prodDao.getPCByCname(prod.getCname());
					} catch (MsgException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
				/*
				 * try{TransactionManager.getInstance().commit();提交事務}
				 * catch(有異常){TransactionManager.getInstance().rollback();回滾事務}
				 * finally{關閉conn連接}
				 */
				
				//------------注意如果在這裏寫int i=10/0; 出現異常,數據庫中插入了商品種類表,但是對應的商品未插入,---
				//-----------所以應該是事務級別,要麼都完成,要麼都不完成------------------------
				
				//2.使用cid給prob賦值
				prod.setCid(pc.getId());
				//3.添加商品信息到prob表
				
				flag1=prodDao.insertPord(prod);
				
				//--------提交事務-----------------
				TransactionManager.getInstance().commit();
		   }catch(Exception e){
			   e.printStackTrace();
				//--------事務回滾-----------------
				TransactionManager.getInstance().rollback();
		   }finally{
				// 關閉連接
				TransactionManager.getInstance().closeConn();
			}
		   return flag1;
	}

	
	// ProdService     	List<Prod>  listProd(){}查詢所有產品的數據
	//查詢全部商品信息的方法
	@Override
	public List<Prod> listAllProd() {
		// TODO Auto-generated method stub
		return prodDao.listAllProd();
	}

}

3.3ProdDao

/**
	 * 查詢全部商品信息的方法
	 * @return 封裝了商品數據的JavaBean的集合 或 null
	 */
	List<Prod> listAllProd();

ProdDaoService

package com.hxuner.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import com.hxuner.domain.Prod;
import com.hxuner.domain.ProdCategory;
import com.hxuner.exception.MsgException;
import com.hxuner.util.JDBCUtils;
import com.hxuner.util.TransactionManager;


public class ProdDaoImpl implements ProdDao {
   
	//根據商品種類名稱查詢商品種類的方法  prob_category表根據cname查詢cid,返回商品種類實體
	@Override
	public ProdCategory getPCByCname(String cname) throws MsgException {
		String sql="select * from prob_category where cname=?";
		Connection conn=null;
		PreparedStatement ps=null;
		ResultSet rs=null;
		
		try {
			conn=TransactionManager.getInstance().getConn();//因爲操作事務,Dao的這個conn對象與Service的conn對象是同一個,所以需要獲取TransactionManager的唯一實例對象conn
															//而且由於是同一個Conn,關閉操作在Service事務完成之後進行關閉。
			ps=conn.prepareStatement(sql);					
			ps.setString(1, cname);
			rs=ps.executeQuery();
			if(rs.next()){
				//如果查詢到數據,則封裝成pc對象,返回給Service
				ProdCategory pc=new ProdCategory();
				pc.setId(rs.getInt("id"));
				pc.setCname(rs.getString("cname"));
				return pc;
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new MsgException("添加商品種類出現異常");
		}
		return null;
	}
   
	
	//向數據庫添加商品種類的方法   prob_category表插入商品種類cname,id
	@Override
	public boolean insertProdCategory(ProdCategory pc) {
		String sql="insert into prob_category values(null,?)";
		Connection conn=null;
		PreparedStatement ps=null;
		try {
			conn=TransactionManager.getInstance().getConn();//因爲操作事務,Dao的這個conn對象與Service的conn對象是同一個,所以需要獲取TransactionManager的唯一實例對象conn
															//而且由於是同一個Conn,關閉操作在Service事務完成之後進行關閉。
			ps=conn.prepareStatement(sql);
			ps.setString(1, pc.getCname());
			int i=ps.executeUpdate();
			if(i>0){
				return true;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}
    
	
	
	//向數據庫內的商品表添加商品  prob表插入prob商品
	@Override
	public boolean insertPord(Prod prod) {
		//cname商品種類名稱,該字段不屬於商品表,但是屬於前臺表單提交數據。因此添加屬性來封裝數據
		//cname不存在在prod商品表中
		String sql="insert into prob values(null,?,?,?,?,?,?)";
		Connection conn=null;
		PreparedStatement ps=null;
		try {
			conn=TransactionManager.getInstance().getConn();  //因爲操作事務,Dao的這個conn對象與Service的conn對象是同一個,所以需要獲取TransactionManager的唯一實例對象conn
															 //而且由於是同一個Conn,關閉操作在Service事務完成之後進行關閉。
			ps=conn.prepareStatement(sql);
			// String double  int  int String String
			ps.setString(1, prod.getName());
			ps.setDouble(2, prod.getPrice());
			ps.setInt(3, prod.getCid());
			ps.setInt(4, prod.getPnum());
			ps.setString(5, prod.getImgurl());
			ps.setString(6, prod.getDescription());
			int i=ps.executeUpdate();
			if(i>0){
				return true;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}

	
	 //ProdDao     	List<Prod>	listProd(){}查詢所有產品的數據
	@Override
	public List<Prod> listAllProd(){
		//不只是需要查詢整個商品的信息,還需要知道商品種類名稱cname,因爲prod的javaBean中有pord_categroy的cname和prod數據庫表的所有字段
		String sql="select p.*,c.cname from prob p inner join prob_category c on p.cid=c.id";
		List<Prod> list=null;
		Connection conn=null;
		PreparedStatement ps=null;
		ResultSet rs=null;
		try {
				conn=JDBCUtils.getConn();
				ps=conn.prepareStatement(sql);
				rs=ps.executeQuery();
				list=new ArrayList<Prod>();
				while(rs.next()){
					Prod prod=new Prod();
					prod.setId(rs.getInt("id"));
					prod.setName(rs.getString("name"));
					prod.setCname(rs.getString("cname"));    //不只是需要查詢整個商品的信息,還需要知道商品種類名稱cname,因爲prod的javaBean中有pord_categroy的cname和prod數據庫表的所有字段
					prod.setCid(rs.getInt("cid"));
					prod.setPrice(rs.getDouble("price"));
					prod.setPnum(rs.getInt("pnum"));
					prod.setImgurl(rs.getString("imgurl"));
					prod.setDescription(rs.getString("description"));
					list.add(prod);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}finally{
				JDBCUtils.close(conn, ps, rs);
			}
			return list;
		
	}
	
	
}

3.4ProdList.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE HTML>
<html>
<head>
	<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
	<link href="css/prodList.css" rel="stylesheet" type="text/css">
</head>
<body>
	<div id="content">
		<div id="search_div">
			<form method="post" action="#">
				<span class="input_span">商品名:<input type="text" name="name"/></span>
				<span class="input_span">商品種類:<input type="text" name="category"/></span>
				<span class="input_span">商品價格區間:<input type="text" name="minprice"/> - <input type="text" name="maxprice"/></span>
				<input type="submit" value="查詢">
			</form>
		</div>
		<%--ProdList.jsp  從request作用域獲取數據並展示 --%>
		<c:forEach items="${requestScope.prods}" var="prod" >
		<div id="prod_content">
			<div id="prod_div">
				<img src="${app}/ProdImageServlet?imgurl=${prod.imgurl}"></img> 
				<%-- <img src="${prod.imgurl }"></img> 由於WEB-INF的圖片不能被瀏覽器訪問,被保護,只能通過response輸出緩衝區獲取 (和獲取驗證碼一樣)
					  ProdList.jsp 		src="ProdImageServlet"傳入imgurl
					  ProdImageServlet 	1.獲取請求參數,得到圖片的url		2.從服務器上獲取該商品的圖片  3.通過應答流寫給用戶
				 --%>
				
				<div id="prod_name_div">
					${prod.name }
				</div>
				<div id="prod_price_div">
					¥ ${prod.price} 元
				</div>
				<div>
					<div id="gotocart_div">
						<a href="#">加入購物車</a>
					</div>					
					<div id="say_div">
						133人評價
					</div>					
				</div>
			</div>
		</div>
		</c:forEach>	
		
		
		<div style="clear: both"></div>
	</div>
</body>
</html>
    

3.5ProdImgurlServlet

package com.hxuner.web;

import java.io.FileInputStream;
import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/*
 	<img src="${prod.imgurl }"></img> 由於WEB-INF的圖片不能被瀏覽器訪問,被保護,只能通過response輸出緩衝區獲取 (和獲取驗證碼一樣)
	ProdList.jsp 		src="ProdImageServlet"傳入imgurl
	ProdImageServlet 	1.獲取請求參數,得到圖片的url		2.從服務器上獲取該商品的圖片  3.通過應答流寫給用戶
*/
public class ProdImageServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		       // 獲取ServletContext對象
				ServletContext sc=this.getServletContext();
				// 1. 獲取請求參數 imgurl
				String imgurl=request.getParameter("imgurl");
				// 2. 打開一個輸入流,從服務器上讀取對應圖片的字節流
				FileInputStream fis=null;
				ServletOutputStream sos=null;
				try {
					fis=new FileInputStream(sc.getRealPath(imgurl));
					sos=response.getOutputStream();	   //獲取應答流response
					byte[] array=new byte[100];
					int len=fis.read(array);
					while(len!=-1){
						// 3. 將圖片寫入應答緩衝區,後續web容器會從緩衝區中拿出該內容,添加到應答實體中,返回給瀏覽器
						sos.write(array, 0, len);
						len=fis.read(array);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}finally{
					if(fis!=null){
						fis.close();
					}
					//注意不用關閉response應答流,會自動關閉
				}
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章