【web第十九天】商品添加功能

商品添加功能

 

1.准备文件上传的后台页面

2.准备数据库中的表:商品表,商品种类表(创建对应的javaBean)

3.准备文件上传表单,修改form中的提交地址到servlet

4.servlet封装数据,并交给service添加商品,跳转到页面(利用工厂模式,  完善配置文件)

           4.1 导包

           4.2 如果是普通字段:存入map

           4.3 如果是文件上传项:将路径存入map

5.service:调用dao添加商品

6.dao进行添加

 

 

 

 

1.搭建easymall后台页面

  将课前资料中的后台页面backend拷贝到WebRoot下,并修改_left.jsp中商品添加<div>:

<div><a href="${ app }/backend/manageAddProd.jsp" target="rightFrame">> 商品添加</a></div>

 

2.设计商品相关表

  在easymall数据库中创建商品种类表和商品表

 

  商品种类表

           create table prod_category(

                     id int primary key auto_increment,        -- 商品种类编号

                     cname varchar(255)                      -- 商品种类名称

           );

                    

   商品表

           create table prod(

                     id int primary key auto_increment,        -- 商品编号

                     name varchar(255),                       -- 商品名称

                     price double,                      -- 商品单价

                     cid int,                                           -- 商品种类编号(外键)

                     pnum int,                                       -- 商品库存

                     imgurl varchar(255),                       -- 图片地址

                     description varchar(255)                     -- 商品描述

           );

3.后台开发

  在bean包中创建类:ProdCategory,添加如下属性并添加get和set方法:

private int id;

private String cname;

  在bean包中创建类:ProdCategory,添加如下属性并添加get和set方法:

private int id;

    private String name;

    private double price;

    private int cid;

    private int pnum;

    private String imgurl;

    private String description;

    private String cname;

 

  在后台页面manageAddProd.jsp中,<form>表单提交地址改为:

<form action="${ app }/servlet/ManageAddProdServlet" method="POST" enctype="multipart/form-data">

 

  在web包中创建包:cn.tedu.web.backend,在此包下创建servlet:ManageAddProdServlet,并在WEB-INF下创建文件夹:temp和upload,分别作为临时文件和商品图片的存放位置,然后导入文件上传相关的jar包:commons-fileupload-1.2.1.jar和commons-io-1.4.jar,在ManageAddProdServlet中添加如下代码:

try {

    ServletContext sc = this.getServletContext();

    Map<String,String> paramMap = new HashMap<String,String>();

    //1.上传商品图片

    //生成文件上传工厂

    String temp = "WEB-INF/temp";

    String upload = "WEB-INF/upload";

    DiskFileItemFactory factory = new DiskFileItemFactory(100,new File(sc.getRealPath(temp)));

            

    //创建文件上传核心类

    ServletFileUpload fileUpload = new ServletFileUpload(factory);

    //判断是否是正确的文件上传表单

    if(!fileUpload.isMultipartContent(request)){

        throw new MsgException("请使用正确的表单上传数据!");

    }

    //设定上传文件的大小限制

    fileUpload.setFileSizeMax(1024*1024);

    fileUpload.setSizeMax(1024*1024);

    //设定编码解决中文文件名乱码

    fileUpload.setHeaderEncoding(sc.getInitParameter("encode"));

            

    //解析请求 获取FileItem集合

    List<FileItem> items = fileUpload.parseRequest(request);

    //处理item

    for (FileItem item : items) {//普通字段项

        if(item.isFormField()){

             String name = item.getFieldName();

             String value = item.getString(sc.getInitParameter("encode"));

             System.out.println(name+":"+value);

             paramMap.put(name, value);

        }else{//文件上传项

             String fname = item.getName();

             //ie文件名bug

             if(fname.contains("\\")){

                 fname = fname.substring(fname.lastIndexOf("\\")+1);

             }

            //文件名重复

             fname = UUID.randomUUID().toString()+"_"+fname;

             //分目录存储,计算存储路径

             String hash = Integer.toHexString(fname.hashCode());

             while(hash.length()<8){

                 hash += "0";

             }

             for (int i = 0; i < hash.length(); i++) {

                 upload += "/"+hash.charAt(i);

             }

             new File(sc.getRealPath(upload)).mkdirs();

             String savePath = sc.getRealPath(upload+"/"+fname);

             paramMap.put("imgurl", upload+"/"+fname);

                    

             //上传文件

             InputStream in = item.getInputStream();

             OutputStream out = new FileOutputStream(savePath);

             byte[] data = new byte[1024];

             int len = -1;

             while((len=in.read(data)) != -1){

                 out.write(data,0,len);

             }

             in.close();

             out.close();

             //删除临时文件

             item.delete();

        }

    }

            

    //2.将商品信息保存到数据库

    ProdService service = BasicFactory.getFactory().getInstance(ProdService.class);

    //创建prod对象封装数据

    Prod prod = new Prod();

    prod.setName(paramMap.get("name"));

    prod.setPrice(Double.parseDouble(paramMap.get("price")));

    prod.setCname(paramMap.get("cname"));

    prod.setPnum(Integer.parseInt(paramMap.get("pnum")));

    prod.setImgurl(paramMap.get("imgurl"));

    prod.setDescription(paramMap.get("description"));

            

    //调用service增加商品

    service.addProd(prod);

            

    //3.提示成功回到管理主页

    response.getWriter().write("恭喜您,商品添加成功!");

    response.setHeader("refresh", "1,url="+request.getContextPath()+"/backend/_right.jsp");

            

} catch (MsgException e) {

    response.getWriter().write(e.getMessage());

    return;

}catch (Exception e) {

    e.printStackTrace();

    throw new RuntimeException();

}

 

    在service包中创建接口:ProdService,并添加如下代码:

/**

     * 添加商品

     * @param prod 封装了商品信息的bean

     * @throws MsgException 消息异常

     */

    void addProd(Prod prod)throws MsgException;

 

    在service包中创建实现类:ProdServiceImpl,实现ProdService接口,并在配置文件:config.properties中添加如下配置信息:

ProdService=cn.tedu.service.ProdServiceImpl

 

    在ProdServiceImpl中添加如下代码:

private ProdDao dao = BasicFactory.getFactory().getInstance(ProdDao.class);

   

public void addProd(Prod prod) throws MsgException {

    //1.根据商品种类名称查询商品种类表

    String cname = prod.getCname();

    ProdCategory findPc = dao.findProdCategoryByCname(cname);

       

    //2.处理商品种类

    int cid = 0;

    if(findPc == null){

        //如果找不到,则向商品种类表中加入新的商品种类

        ProdCategory pc = new ProdCategory();

        pc.setCname(cname);

        dao.addProdCategory(pc);

        //并获取新增加的商品的id 作为外键保存在商品信息里

        ProdCategory findPc2 = dao.findProdCategoryByCname(cname);

        cid = findPc2.getId();

    }else{

        //如果查找到,则使用该商品种类的id作为外键保存在商品信息里

        cid = findPc.getId();

    }

       

    //3.将商品信息加入商品表

    prod.setCid(cid);

    dao.addProd(prod);

}

 

      在dao包中创建接口:ProdDao,并添加如下代码:

/**

     * 根据商品种类名称查询商品种类信息

     * @param cname 商品种类名称

     * @return 查找到的商品种类信息,如果找不到就返回null

     */

    ProdCategory findProdCategoryByCname(String cname);

    /**

     * 向商品种类表中增加记录

     * @param pc 封装了商品种类信息的bean

     */

    void addProdCategory(ProdCategory pc);

    /**

     * 将商品信息加入数据库

     * @param prod 封装了商品信息的bean

     */

    void addProd(Prod prod);

 

      在dao包下创建实现类:ProdDaoImpl,实现ProdDao接口,并在配置文件:config.properties中添加如下配置信息:

ProdDao=cn.tedu.dao.ProdDaoImpl

 

      在ProdDaoImpl中添加如下代码:

public ProdCategory findProdCategoryByCname(String cname) {

        Connection conn = null;

        PreparedStatement ps = null;

        ResultSet rs = null;

        try{

             conn = JDBCUtils.getConnection();

             String sql = "select * from prod_category where cname = ?";

             ps = conn.prepareStatement(sql);

             ps.setString(1, cname);

             rs = ps.executeQuery();

             if(rs.next()){

                 ProdCategory pc = new ProdCategory();

                 pc.setId(rs.getInt("id"));

                 pc.setCname(rs.getString("cname"));

                 return pc;

             }else{

                 return null;

             }

        }catch (Exception e) {

             e.printStackTrace();

             throw new RuntimeException(e);

        } finally {

             JDBCUtils.close(conn, ps, rs);

        }

    }

 

    public void addProdCategory(ProdCategory pc) {

        Connection conn = null;

        PreparedStatement ps = null;

        ResultSet rs = null;

        try{

             conn = JDBCUtils.getConnection();

             String sql = "insert into prod_category values (null,?)";

             ps = conn.prepareStatement(sql);

             ps.setString(1, pc.getCname());

             ps.executeUpdate();

        }catch (Exception e) {

             throw new RuntimeException(e);

        } finally {

             JDBCUtils.close(conn, ps, rs);

        }

    }

 

    public void addProd(Prod prod) {

        Connection conn = null;

        PreparedStatement ps = null;

        ResultSet rs = null;

        try{

             conn = JDBCUtils.getConnection();

             String sql = "insert into prod values (null,?,?,?,?,?,?)";

             ps = conn.prepareStatement(sql);

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

             ps.executeUpdate();

        }catch (Exception e) {

             throw new RuntimeException(e);

        } finally {

             JDBCUtils.close(conn, ps, rs);

        }

    }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

事务

1.事务的概念

  事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功。

  例如:A——B转帐,对应于如下两条sql语句

    update account set money=money-100 where name=‘a’;

    update account set money=money+100 where name=‘b’;

  在这个例子中,我们要保证这两条sql要么一起成功,要么一起失败,不允许一部分成功一部分失败,这就要靠数据库的事务来实现了。

2.管理事务

2.1.数据库默认的事务

  数据库默认支持事务的,但是数据库默认的事务是一条sql语句独占一个事务,这种模式意义不大。

2.2.手动控制事务

2.2.1.sql控制事务

  start transaction;

  开启事务,在这条语句之后的所有的sql将处在同一事务中,要么同时完成要么同时不完成。

  事务中的sql在执行时,并没有真正修改数据库中的数据。

 

  commit;

  提交事务,将整个事务对数据库的影响一起发生。

   

  rollback;

  回滚事务,将这个事务对数据库的影响取消掉。

2.2.2.JDBC中控制事务

  当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列语句:

          

  conn.setAutoCommit(false);

    关闭自动连接后,conn将不会帮我们提交事务,在这个连接上执行的所有sql语句将处在同一事务中,需要我们是手动的进行提交或回滚

  conn.commit();

    提交事务

  conn.rollback();

    回滚事务

 

  也可以设置回滚点回滚部分事务。

  SavePoint sp = conn.setSavePoint();

  conn.rollback(sp);

    注意:回到回滚点后,回滚点之前的代码虽然没被回滚但是也没提交呢,如果想起作用还要做commit操作。

3.事务的四大特性

  事务的四大特性是事务本身具有的特点。简称ACID。

    原子性(Atomicity

      原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 

    一致性(Consistency

      事务前后数据的完整性必须保持一致。
    隔离性(Isolation

      事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。

    持久性(Durability

      持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

4.隔离性

4.1.数据库隔离性分析

  数据库的其他三大特性数据库可以帮我们保证,而隔离性我们需要再讨论。

  我们知道数据库的隔离性问题本质上就是多线程并发安全性问题。

  可以用锁来解决多线程并发安全问题,但是如果用了锁,必然会造成程序的性能大大的下降.对于数据库这种高并发要求的程序来说这是不可接受的.

 

  我们可以具体分析下隔离性产生的细节:

  如果两个线程并发修改,必然产生多线程并发安全问题,必须隔离开

  如果两个线程并发查询,必然没有问题,不需要隔离

  如果一个线程修改,一个线程查询,在不同的应用场景下有可能有问题,有可能没问题。

4.2.隔离性可能造成的问题

4.2.1.脏读:

  打开两个mysql客户端,都执行以下语句。

  set  session transaction isolation level read uncommitted;

 

  一个事务读取到另一个事务未提交的数据:a买鞋,b卖鞋

           ----------------------------

           a        1000

           b        1000

           ----------------------------

           客户端a:

                     start transaction;

                     update account set money = money-100 where name='a';

                     update account set money = money+100 where name='b';

                     -----------------------------

                     客户端b:

                                start transaction;

                                select * from account;

                                a 900

                                b 1100

                                commit;

                     -----------------------------

                     客户端a:

                     rollback;

                     -----------------------------

                     客户端b:

                                start transaction;

                                select * from account;

                                a 1000

                                b 1000

                                commit;

                     -----------------------------

4.2.2.不可重复读:

一个事务多次读取数据库中的同一条记录,多次查询的结果不同(一个事务读取到另一个事务已经提交的数据)  a:银行账户,W:银行工作人员(领导让W统计a的账户情况)

           ------------------------------

        活期    定期    固定资产

           a        1000         1000         1000

           ------------------------------

           W:

                     start transaction;

                     select 活期 from account where name='a'; -- 活期存款:1000W元

                     select 定期 from account where name = 'a'; -- 定期存款:1000W元

                     select 固定 from account where name = 'a'; -- 固定资产:1000W元

                     ---------------------------

                     a:

                                start transaction;

                                update account set 活期=活期-1000 where name= 'a';

                                commit;

                     ---------------------------

select 活期+定期+固定 from account where name='a'; ---总资产:2000W元

4.2.3.虚读(幻读)

有可能出现,有可能不出现,概率非常低:一个事务多次查询整表数据,多次查询时,由于有其他事务增删数据, 造成的查询结果不同(一个事务读取到另一个事务已经提交的数据)

           ------------------------------

           a        1000        

           b        2000

           ------------------------------

 

           工作人员d:

           start transaction;

           select sum(money) from account; --- 总存款3000元

           select count(*) from account; --- 总账户数2个

                     -----------------

                     c:

                                start transaction;

                                insert into account values (null,'c',3000);

                                commit;

                     -----------------

           select avg(mone) from account; --- 平均每个账户:2000元

5.数据库的隔离级别

数据库设计者在设计数据库时到底该防止哪些问题呢?防止的问题越多性能越低,防止的问题越少,则安全性越差。

到底该防止哪些问题应该由数据库使用者根据具体的业务场景来决定,所以数据库的设计者并没有把放置哪类问题写死,而是提供了如下选项:

 

数据库的四大隔离级别:

read uncommitted;

不做任何隔离,可能造成脏读、不可重复度、虚读(幻读)问题

read committed;

可以防止脏读,但是不能防止不可重复度、虚读(幻读)问题

repeatable Read;

           可以防止脏读、不可重复度,但是不能防止虚读(幻读)问题

serializable;

           可以防止所有隔离性的问题,但是数据库就被设计为了串行化的数据库,性能很低

 

从安全性上考虑:

Serializable > Repeatable Read > Read Committed > Read uncommitted

从性能上考虑:

Read uncommitted > Read committed > Repeatable Read > Serializable

 

我们作为数据库的使用者,综合考虑安全性和性能,从四大隔离级别中选择一个在可以防止想要防止的问题的隔离级别中性能最高的一个。

其中Serializable性能太低用的不多,Read uncommitted安全性太低用的也不多,我们通常从Repeatable Read和Read committed中选择一个。

如果需要防止不可重复读选择Repeatable Read,如果不需要防止选择Read committed

 

mysql数据库默认的隔离级别就是Repeatable Read

Oracle数据库默认的隔离级别是Read committed

 

6.操作数据库的隔离级别

查询数据库的隔离级别:

select @@tx_isolation;

修改数据库的隔离级别:

set [session/global] transaction isolation level xxxxxx;

 

不写默认就是session,修改的是当前客户端和服务器交互时是使用的隔离级别,并不会影响其他客户端的隔离级别

如果写成global,修改的是数据库默认的隔离级别(即新开客户端时,默认的隔离级别),并不会修改当前客户端和已经开启的客户端的隔离级别

set global transaction isolation level serializable;

7.数据库中的锁

7.1.共享锁

共享锁和共享锁可以共存,共享锁和排他锁不能共存.在非Serializable隔离级别下做查询不加任何锁,在Serializable隔离级别下做查询加共享锁。

案例演示:打开两个mysql客户端,将隔离级别都设置为Serializable级别,

set session transaction isolation level Serializable;--设置后查询加了共享锁

分别在两个客户端中查询:

start  transaction;

select * from account;--都能查询出数据,说明共享锁可以共存。

7.2.排他锁

排他锁和共享锁不能共存,排他锁和排他锁也不能共存,在任何隔离级别下做增删改都加排他锁。

在7.1的基础上,在其中一个客户端执行修改操作,将一个客户端的共享锁升级为排他锁:

两个客户端都执行:

start  transaction;

select * from account;

-----------------

一个客户端执行:

update account set money = 900;-- #发现执行在等待,当另外一个客户端提交commit或者回滚rollback之后,修改才能成功。

 

另外一个客户端执行:

  rollback/commit;

7.3.死锁

mysql可以自动检测到死锁,错误退出一方并执行另一方。

在7.1基础上:

两个客户端都执行:

start  transaction;

select * from account;

-----------------

一个客户端执行:

update account set money = 900;

另外一个客户端执行:

update account set money = 800;

发现彼此等待,直到一方报错结束,死锁才结束。

8.EasyMall添加商品 - 事务控制

事务应该加在业务层还是dao层?

因为dao层的一个方法就是一个C/R/U/D其中的一个操作,所以需要将事务的代码写在业务层的实现类中。

但是业务层获取的conn,和dao层的conn是各自的数据库连接,他们并没有共享在业务层获取的数据库连接,所以在调用方法时,应该将数据库连接对象作为参数传递过去。

 

EasyMall添加商品事务控制代码示例:(版本一)

在ProdServiceImpl中添加事务控制代码,并将此connection通过参数传递到dao层,添加或修改如下代码:

private ProdDao dao = BasicFactory.getFactory().getInstance(ProdDao.class);

   

    public void addProd(Prod prod) throws MsgException {

        Connection conn = null;

        try {

             //开启事务

             conn = JDBCUtils.getConnection();

             conn.setAutoCommit(false);

             //1.根据商品种类名称查询商品种类表

             String cname = prod.getCname();

             ProdCategory findPc = dao.findProdCategoryByCname(conn,cname);

            

             //2.处理商品种类

             int cid = 0;

             if(findPc == null){

                 //如果找不到,则向商品种类表中加入新的商品种类

                 ProdCategory pc = new ProdCategory();

                 pc.setCname(cname);

                 dao.addProdCategory(pc);

                 //并获取新增加的商品的id 作为外键保存在商品信息里

                 ProdCategory findPc2 = dao.findProdCategoryByCname(conn,cname);

                 cid = findPc2.getId();

             }else{

                 //如果查找到,则使用该商品种类的id作为外键保存在商品信息里

                 cid = findPc.getId();

             }

            

             //3.将商品信息加入商品表

             prod.setCid(cid);

             dao.addProd(conn,prod);

             //提交事务

             conn.commit();

        } catch (SQLException e) {

             //回滚事务

             try {

                 if(conn != null){

                     conn.rollback();

                 }

             } catch (SQLException e1) {

                 e1.printStackTrace();

             }

             e.printStackTrace();

             throw new RuntimeException();

        }

    }

 

Dao层代码也需要为之改变(不做演示)。

 

此代码虽然可以解决事务控制问题,但是代码耦合性大

Service要控制事务,事务需要Connection对象,Connection对象是Dao层特有的对象,Service不应该持有Connection。

 

Service不能用Connection,又要控制事务,矛盾了,怎么办?

有些耦合性无法避免 --> 想办法管理起来 --> 将所有控制事务的代码整合到一个工具类中来进行管理

 

开发TransactionManager,在其中管理Connection,并对外提供getConn、startTran、commitTran、rollbackTran、release方法。

之后所有和事务 相关的操作都不要直接使用Conn 而是通过TransactionManager来实现管理

解决耦合性的问题 - 本质上是将耦合转移到了TransactionManager中同一管理。

虽然没有彻底的解决耦合,但是统一管理起来,方便未来开发和维护。

 

EasyMall添加商品事务控制代码示例:(版本二)

在utils包中创建类:TransactionManager,并添加如下代码:

private TransactionManager(){

    }

    private static Connection conn = null;

    static{

        conn = JDBCUtils.getConnection();

    }

    /**获取数据库连接的方法

     * @return 数据库连接对象

     */

    public static Connection getConn(){

        return conn;

    }

    /**开启事务

     */

    public static void startTran(){

        try {

             conn.setAutoCommit(false);

        } catch (SQLException e) {

             e.printStackTrace();

             throw new RuntimeException();

        }

    }

    /**提交事务

     */

    public static void commitTran(){

        try {

             conn.commit();

        } catch (SQLException e) {

             e.printStackTrace();

             throw new RuntimeException();

        }

    }

    /**回滚事务

     */

    public static void rollbackTran(){

        try {

             conn.rollback();

        } catch (SQLException e) {

             e.printStackTrace();

             throw new RuntimeException();

        }

    }

/**释放资源

     */

    public static void release(){

        JDBCUtils.close(conn, null, null);

    }

 

在ProdServiceImpl中修改代码如下:

private ProdDao dao = BasicFactory.getFactory().getInstance(ProdDao.class);

   

    public void addProd(Prod prod) throws MsgException {

        try {

             //开启事务

             TransactionManager.startTran();

             //1.根据商品种类名称查询商品种类表

             String cname = prod.getCname();

             ProdCategory findPc = dao.findProdCategoryByCname(cname);

             //2.处理商品种类

             int cid = 0;

             if(findPc == null){

                 //如果找不到,则向商品种类表中加入新的商品种类

                 ProdCategory pc = new ProdCategory();

                 pc.setCname(cname);

                 dao.addProdCategory(pc);

                 //并获取新增加的商品的id 作为外键保存在商品信息里

                 ProdCategory findPc2 = dao.findProdCategoryByCname(cname);

                 cid = findPc2.getId();

             }else{

                 //如果查找到,则使用该商品种类的id作为外键保存在商品信息里

                 cid = findPc.getId();

             }

             //3.将商品信息加入商品表

             prod.setCid(cid);

             dao.addProd(prod);

             //提交事务

             TransactionManager.commitTran();

        } catch (Exception e) {

             //回滚事务

             TransactionManager.rollbackTran();

             e.printStackTrace();

             throw new RuntimeException();

        }finally{

             //关闭数据库链接

             TransactionManager.release();

        }

    }

 

在ProdDaoImpl中,将获取conn的代码从JDBCUtils.getConnection()改为从TransactionManager中获取,例如:

conn = TransactionManager.getConn();

 

将所有关闭数据库链接的操作中,不要将链接conn关闭,代码改为:

} finally {

       JDBCUtils.close(null, ps, rs);

}

 

测试发现,当添加完商品再次添加后,就会抛出异常:

SQLException: You can't operate on a closed Connection!!!

由于第三方管理类中,连接定义成了一个静态的,整个项目中共用一个数据库连接对象,添加完订单之后,关闭了该连接对象,再次查询全部商品时,使用的连接对象是一个已经关闭的数据库连接对象,所以抛出以上异常。

 

经过分析要想解决问题,需要让每一个服务的线程中保存一个独立数据库连接对象。

9.ThreadLocal:本地线程变量(重要!)

9.1.ThreadLocal概述

在线程内部保存数据,利用线程对象在线程执行的过程中传递数据,另外由于每个线程各自保存各自的数据conn,所以可以避免线程并发安全问题。

Thread对象内置了一个Map来存取消息,但是这个map外界无法直接操作,需要通过ThreadLocal来实现对Thread中的Map进行数据的存取。

9.2.ThreadLocal方法

ThreadLocal tl = new ThreadLocal();

 

tl.set(obj);       --向当前线程中的map保存对象,key为当前线程变量,值为存入的数据。

tl.get();              --从当前线程中获取对象,如果获取不到对象,调用initialValue()创建一个新的对象。

tl.remove();    --从当前线程中删除对象。

initialValue()     --创建对象。

 

EasyMall添加商品事务控制代码示例:(版本三:最终)

在TransactionManager中,加入ThreadLocal代码,通过本地线程变量获取conn,代码如下:

private TransactionManager(){

    }

   

    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(){

        protected Connection initialValue() {

             return JDBCUtils.getConnection();

        };

    };

   

    /**获取数据库连接的方法

     * @return 数据库连接对象

     */

    public static Connection getConn(){

        return tl.get();

    }

    /**开启事务

     */

    public static void startTran(){

        try {

             tl.get().setAutoCommit(false);

        } catch (SQLException e) {

             e.printStackTrace();

             throw new RuntimeException();

        }

    }

    /**提交事务

     */

    public static void commitTran(){

        try {

             tl.get().commit();

        } catch (SQLException e) {

             e.printStackTrace();

             throw new RuntimeException();

        }

    }

    /**回滚事务

     */

    public static void rollbackTran(){

        try {

             tl.get().rollback();

        } catch (SQLException e) {

             e.printStackTrace();

             throw new RuntimeException();

        }

    }

    /**释放资源

     */

    public static void release(){

        JDBCUtils.close(tl.get(), null, null);

        //从本地线程变量中删除

        tl.remove();

    }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2018-03-29 21:33  songyao  阅读(354)  评论(0编辑  收藏  举报