- 多表连接查询
表与表之间的关系:
在数据库中有 一对一,一对多,多对一,多对多。
针对每一张表,就只有二个映射基数。(一 和 多)
在 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 对多 : 在实体类中就是一个 集合。
案例如下:
- 一个商品对应一个类型:
//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; //一个商品对应一种类型
}
- 一种类型对应多个商品:
//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 类中,要处理如下三件事:
- 异常处理
- 事务处理
- 资源释放(连接关闭)
- 事务是什么
事务是数据库技术中的一个特性。
事务表示是一组不可分割的整体操作。
事务正常操作就全体执行。否则就全体不执行。
例如:转账是一个事务。
完整的转账操作有二步:
- A+1000
- B-1000
转账的两个操作就应该是一个事务整体。
在数据库中操作事务三个动作:
- 打开事务
- 以提交方式关闭事务 确定事务过程中的修改
- 以回滚方式关闭事务 撤消事务过程中的修改
- 在 JDBC 中操作事务
第一点:同一个事务必须使用相同的Connection对象。
第二点:在 JDBC 中事务操作共三个方法。
- 打开事务 conn.setAutoCommit(false); //关闭自动提交事务
- 提交事务 conn.commit(); //手动提交事务
- 回滚事务 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()+"保存失败!请重新输入!");
}
}
}