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);
	}

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值