0308-JDBC技术二、多表连接查询、Service层、事务

  • 多表连接查询

表与表之间的关系:

在数据库中有 一对一,一对多,多对一,多对多。
针对每一张表,就只有二个映射基数。(一 和 多)

在 Java 中,一对多和多对一不是一个事。
表被映射为一个类。

类与类之间如何表示数据库中的映射基数,
一 : 对象
多 : 集合


编写映射 Type_no5 表的实体类:

本次我们先不去讨论 自连接。
在这里插入图片描述
编写实体类:

//type_no5表
public class Types {
    //    TYPE_ID  NUMBER(7,0)
    private Integer typeId;
    //    TYPE_NAME    VARCHAR2(20 BYTE)
    private String typeName;
    //    TYPE_PID NUMBER(7,0)
    private Integer typePid;
    //    TYPE_LEVEL   NUMBER(7,0)
    private Integer typeLevel;
    //    TYPE_PATH    VARCHAR2(2000 BYTE)
    private String typePath;
}

在实体类之间去表示数据表之间的关系:

在数据库中整理一下:就二种: X对一,X对多。

X 对一 : 在实体类中就是一个 对象。
X 对多 : 在实体类中就是一个 集合。
案例如下:

  1. 一个商品对应一个类型:
//Goods_no5
public class Goods {
    //    GOODS_ID NUMBER(7,0)
    private Integer goodsId;
    //    GOODS_NAME   VARCHAR2(200 BYTE)
    private String goodsName;
    //    GOODS_PRICE  NUMBER(7,2)
    private Double goodsPrice;
    //    GOODS_DESC   VARCHAR2(2000 BYTE)
    private String goodsDesc;
    //    GOODS_TYPE_ID    NUMBER(7,0)
    private Types goodsType;  //一个商品对应一种类型
}
  1. 一种类型对应多个商品:
//type_no5表
public class Types {
    //    TYPE_ID  NUMBER(7,0)
    private Integer typeId;
    //    TYPE_NAME    VARCHAR2(20 BYTE)
    private String typeName;
    //    TYPE_PID NUMBER(7,0)
    private Integer typePid;
    //    TYPE_LEVEL   NUMBER(7,0)
    private Integer typeLevel;
    //    TYPE_PATH    VARCHAR2(2000 BYTE)
    private String typePath;
    // 类型有哪些商品?    一种类型对应多个商品
    private List<Goods> goodsList;
}

编写 GoodsDAO:

必须使用 连接查询 ,才能在结果集中获取到商品表与类型表 , 两张表的字段。

select * from goods_no5 left join type_no5 on goods_type_id = type_id;

GoodsDAO 中:

public List<Goods> selectByName(String goodsName) throws SQLException {
    List<Goods> goodsList = new ArrayList<>();
    String sql = "select * from goods_no5 " +
            " left join type_no5 on goods_type_id = type_id " +
            " where goods_name like ? ";
    Connection conn = ConnUtils.getConn();
    PreparedStatement pstat = conn.prepareStatement(sql);
    pstat.setString(1,"%"+goodsName+"%");
    ResultSet rs = pstat.executeQuery();
    while(rs.next()){
        Goods goods = new Goods();
        Types goodsType = new Types();
        goods.setGoodsId(rs.getInt("GOODS_ID"));
        goods.setGoodsName(rs.getString("GOODS_NAME"));
        goods.setGoodsPrice(rs.getDouble("GOODS_PRICE"));
        goods.setGoodsDesc(rs.getString("GOODS_DESC"));
        goodsType.setTypeId(rs.getInt("GOODS_TYPE_ID"));
        goodsType.setTypeName(rs.getString("TYPE_NAME"));
        goodsType.setTypeLevel(rs.getInt("TYPE_LEVEL"));
        goodsType.setTypePath(rs.getString("TYPE_PATH"));
        goodsType.setTypePid(rs.getInt("TYPE_PID"));
        goods.setGoodsType(goodsType);//维护关系
        goodsList.add(goods);
    }
    return goodsList;
}

保存一个商品的方法:

/**
 * 向商品表中插入记录的方法
 * @param goods 商品对象
 * @throws SQLException
 */
public void insert(Goods goods) throws SQLException {
    String sql = "insert into " +
            " goods_no5(GOODS_ID,GOODS_NAME,GOODS_PRICE,GOODS_DESC,GOODS_TYPE_ID) " +
            " values(seq01.nextval,?,?,?,?)";
    Connection conn = ConnUtils.getConn();
    PreparedStatement pstat = conn.prepareStatement(sql);
    pstat.setString(1,goods.getGoodsName());
    pstat.setDouble(2,goods.getGoodsPrice());
    pstat.setString(3,goods.getGoodsDesc());
    pstat.setInt(4,goods.getGoodsType().getTypeId());
    pstat.executeUpdate();
}

使用 JUnit 进行单元测试:

public class GoodsDAOTest {
    private GoodsDAO goodsDAO = new GoodsDAO();

    @Test
    public void insert() throws SQLException {
        Types goodsType = new Types();
        goodsType.setTypeId(2);//在数据库中维护关系只靠主键。

        Goods goods = new Goods();
        goods.setGoodsDesc("小心别买错!");
        goods.setGoodsName("雷碧");
        goods.setGoodsPrice(3.0);
        goods.setGoodsType(goodsType);
        goodsDAO.insert(goods);
    }
}

  • 分层结构的业务层-Service

编写 Service 类时,要处理哪些内容?

在我们编写的 Service 类中,要处理如下三件事:

  1. 异常处理
  2. 事务处理
  3. 资源释放(连接关闭)

  • 事务是什么

事务是数据库技术中的一个特性。
事务表示是一组不可分割的整体操作。
事务正常操作就全体执行。否则就全体不执行。

例如:转账是一个事务。
完整的转账操作有二步:

  1. A+1000
  2. B-1000

转账的两个操作就应该是一个事务整体。

在数据库中操作事务三个动作:

  1. 打开事务
  2. 以提交方式关闭事务 确定事务过程中的修改
  3. 以回滚方式关闭事务 撤消事务过程中的修改

  • 在 JDBC 中操作事务

第一点:同一个事务必须使用相同的Connection对象
第二点:在 JDBC 中事务操作共三个方法

  1. 打开事务 conn.setAutoCommit(false); //关闭自动提交事务
  2. 提交事务 conn.commit(); //手动提交事务
  3. 回滚事务 conn.rollback(); //回滚事务

PS:conn.setAutoCommit(false) 是关闭自动提交事务; 在 JDBC 中每一次数据库操作都默认是一个事务,会进行自动事务提交。

第三点:在 JDBC 中查询默认不需要事务。增删改必须写事务


  • 编写 GoodsSerivce 类

创建 GoodsService 类。
创建依赖的 GoodsDAO 的对象。

public class GoodsService {
    private GoodsDAO goodsDAO = new GoodsDAO();
}

编写查询所有商品的业务方法:

public class GoodsService {
    private GoodsDAO goodsDAO = new GoodsDAO();

    public List<Goods> searchAll() {
        try {
            return this.goodsDAO.selectAll();
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            try {
                ConnUtils.closeConn();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

编写删除商品的业务方法:

------删除方法,以后在实际开发中没事别用

public class GoodsService {
    private GoodsDAO goodsDAO = new GoodsDAO();

    public void deleteById(Integer goodsId){
        try {
        	//在更新数据的语句之前,打开事务
            ConnUtils.getConn().setAutoCommit(false);
            this.goodsDAO.deleteById(goodsId);
            //提交事务
            ConnUtils.getConn().commit();
        } catch (Exception e) {
            e.printStackTrace();
            try {
            	//遇到异常以后,回滚事务
                ConnUtils.getConn().rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            throw  new RuntimeException(e);
        } finally {
            try {
                ConnUtils.closeConn();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

  • 编写增加商品的业务方法

增加商品有 业务规则:相同的商品名称不能增加。
如何判断一个商品名称是不是重复?商品名称在数据库中存在不存在?

方法是:
------使用商品名称去查询数据库。

编写 GoodsDAO 中按商品名称查询商品对象:

/**
 * 按商品名称查询商品对象的方法
 * @param goodsName 商品名称
 * @return 商品对象,如果没有找到返回null
 * @throws SQLException
 */
public Goods selectByGoodsName(String goodsName) throws SQLException {
    String sql = "select * from goods_no5 " +
            " left join type_no5 on goods_type_id = type_id " +
            " where goods_name = ? ";
    Connection conn = ConnUtils.getConn();
    PreparedStatement pstat = conn.prepareStatement(sql);
    pstat.setString(1,goodsName );
    ResultSet rs = pstat.executeQuery();
    if(rs.next()){
        Goods goods = new Goods();
        Types goodsType = new Types();
        goods.setGoodsId(rs.getInt("GOODS_ID"));
        goods.setGoodsName(rs.getString("GOODS_NAME"));
        goods.setGoodsPrice(rs.getDouble("GOODS_PRICE"));
        goods.setGoodsDesc(rs.getString("GOODS_DESC"));
        goods.setGoodsType(goodsType);//维护关系
        goodsType.setTypeId(rs.getInt("GOODS_TYPE_ID"));
        goodsType.setTypeName(rs.getString("TYPE_NAME"));
        goodsType.setTypeLevel(rs.getInt("TYPE_LEVEL"));
        goodsType.setTypePath(rs.getString("TYPE_PATH"));
        goodsType.setTypePid(rs.getInt("TYPE_PID"));
        return goods;
    }else{
        return null;
    }
}

编写自定义异常类 NameException:

业务有例外:当商品名称重复时不能增加。

public class NameException extends Exception{
    public NameException(String message) {
        super(message);
    }
}

编写 GoodsService 类中增加方法:

public class GoodsService {
    private GoodsDAO goodsDAO = new GoodsDAO();

    /**
     * 保存新商品的方法
     * @param goods 新商品对象
     * @throws NameException 当商品名称已经存在时,抛出异常。
     */
    public void save(Goods goods) throws NameException {
        try {
            if (this.goodsDAO.selectByGoodsName(goods.getGoodsName()) == null) {
                ConnUtils.getConn().setAutoCommit(false);
                this.goodsDAO.insert(goods);
                ConnUtils.getConn().commit();
            } else {
                throw new NameException("商品名称已经存在!");
            }
        } catch (NameException e) {
            e.printStackTrace();
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
            try {
                ConnUtils.getConn().rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            throw new RuntimeException(e);
        } finally {
            try {
                ConnUtils.closeConn();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

编写测试类:

public class GoodsServiceTest {
    private GoodsService goodsService = new GoodsService();
    @Test
    public void save(){
        Goods goods = new Goods();
        goods.setGoodsPrice(5000.0);
        goods.setGoodsName("入门机单反");
        goods.setGoodsDesc("除了贵点没什么优点!");
        Types goodsType = new Types();
        goodsType.setTypeId(4);
        goods.setGoodsType(goodsType);
        try {
            goodsService.save(goods);
            System.out.println("商品保存成功!");
        } catch (NameException e) {
            e.printStackTrace();
            System.out.println(e.getMessage()+"保存失败!请重新输入!");
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值