这些笔记将引导您完成拍卖系统的设计和构建。
这个系统相当大,所以我不打算在这些注释中介绍系统构造的每一个细节。相反,我将对最重要的系统细节给出一个高层次的概述。
完整的拍卖系统使用了大约27种不同的职业。由于涉及到大量的类,我使用了一个包系统来帮助组织一切:
拍卖
包中只包含AuctionApplication
类。auction.core
包包含的核心类直接反映了数据库中最重要的表和视图的结构。这个包包含了这些类拍卖
,报价
,配置文件
,购买
,航运
,用户
。这些类中的每一个还配备了一个相应的RowMapper类。auction.core
包还包含赢博体育程序的三个存储库:AuctionDAO
,PurchaseDAO
,UserDAO
。这三个存储库都使用JdbcTemplate对象与数据库通信。auction.interfaces
包包含赢博体育程序的三个REST控制器:AuctionController
,PurchaseController
,用户控件
.auction.interfaces.dtos
包中包含额外的数据传输对象类。我将在下面详细介绍这些类。auction.services
包包含单个服务类PasswordService
我在之前的课堂讲稿中讨论过的。这个赢博体育程序中的赢博体育数据库交互都通过JdbcTemplate对象。JdbcTemplate类提供了几个方法来进行数据库交互。在下面的注释中,我将展示我如何使用以下每种方法的例子:
queryForObject ()
查询()
queryForList ()
update ()
前三个方法都与SQL选择语句结合使用,而第四个方法用于赢博体育其他类型的语句,如插入、更新和删除。
这个例子我们在之前的课堂讲稿中已经见过了。这是UserDAO在数据库中存储新用户的方法的代码:
public String save(User User){//首先确保这不是一个重复字符串sql = "SELECT * FROM users WHERE name=?";RowMapper<User> RowMapper = new UserRowMapper();用户旧= null;try {old = jdbcTemplate。查询forobject (sql, rowMapper, user.getName());} catch(Exception ex) {} if(old != null)返回“Duplicate”;//让MySQL生成唯一的id字符串idSQL = "select uuid()";String key = null;尝试{key = jdbcTemplate。queryForObject (idSQL String.class);} catch(Exception ex) {key = "Error";} if(key.equals("Error"))返回key;String hash = passwordService.hashPassword(user.getPassword());字符串insertSQL = "插入到用户(userid,name,password)的值(?, ?, ?);jdbcTemplate.update (insertSQL键,user.getName(),散列);返回键;}
这个例子展示了queryForObject()方法的两个略有不同的赢博体育程序。第一个赢博体育程序更为典型:我们执行一个SQL选择语句,该语句旨在从数据库返回0或1个User对象。在queryForObject()的那个版本中,第二个参数是一个RowMapper,用于生成我们期望从查询中返回的对象类型。queryForObject()的第二个赢博体育程序处理旨在返回字符串的select语句。对于那个版本的queryForObject(),我们只需使用第二个参数来表明我们期望查询返回一个Java String对象。
这个例子还展示了update()方法的一个典型赢博体育。这里我们使用update()来执行SQL插入语句。
我们将要执行的一些查询将被设计为返回多个对象。对于这些类型的查询,我们使用query()方法。下面是一个典型的例子:下面的代码是AuctionDAO的一个方法,它获取赢博体育当前打开的拍卖的列表:
public List<Auction> findActive() {String sql = "select * from auctions where open <=curdate() and close >=curdate()";列表<拍卖>结果= null;RowMapper<Auction> RowMapper = new AuctionRowMapper();尝试{结果= jdbcTemplate。查询(sql, rowMapper);} catch(Exception ex) {results = new ArrayList<Auction>();}返回结果;}
最后,有一些情况需要从数据库中读取不需要使用RowMapper的内容列表。在这些情况下,我们使用queryForList()来代替query()。我们可以在代码中看到一个示例,用于读取特定用户发布的赢博体育拍卖。我定义的Auction类有一个成员变量,该变量存储拍卖的标签列表。有时,当我从数据库中读取一个Auction对象时,我也会到数据库中的tags表中获取与该拍卖相关的标签列表。由于标签是简单的字符串,我将使用queryForList()来读取它们。下面是AuctionDAO类的方法,它读取特定用户发布的赢博体育拍卖:
public List<Auction> findByUser(String userid) {String sql = "select * from auctions where seller=?";列表<拍卖>结果= null;RowMapper<Auction> RowMapper = new AuctionRowMapper();尝试{结果= jdbcTemplate。查询(sql, rowMapper, userid)} catch (Exception ex) {results = new ArrayList<Auction>();} for (Auction a: results){字符串tagSQL = “从拍卖的标签中选择标签”;List<String> tags = jdbcTemplate。queryForList(tagSQL, String.class, a.g etautionid ());a.setTags(标签);}返回结果;}
拍卖赢博体育程序的最终版本支持广泛的API请求。API中有23种不同的交互,分布在三个独立的控制器类中。在之前的课堂讲稿中,我们已经看到了POST和GET交互的几个基本示例。在这些笔记中,我将讨论一些我们以前没有见过的API方法的例子。
拍卖API中的许多方法都使用了路径参数。下面是一个典型的例子:为了获取由特定用户上传的赢博体育Auction对象的列表,我们使用HTTP组合
GET /用户/ < id > /拍卖
其中
@GetMapping("/{id}/auctions") public ResponseEntity<List<Auction>> getAuctions(@PathVariable String id) {List<Auction> results = auctionDAO.findByUser(id);返回ResponseEntity.ok () .body(结果);}
这里有两点很重要。首先,在@GetMapping注释中,我们使用特殊的符号{id}来表示URL中存在路径参数。其次,我们向该方法添加一个带@PathVariable注释的参数,以接收该路径参数的值。
我很少使用的另一个特性是查询参数。正如我在API设计指南注释中所解释的,查询参数很少用作过滤机制的一部分。在拍卖API中有一个这样的例子,对AuctionsController的GET请求获取赢博体育带有特定标签的拍卖:
@GetMapping(params= {"tag"}) public ResponseEntity<List<Auction b> > findActiveAuctionsByTag(@RequestParam(value = "tag") String tag) {List<Auction> auctions = dao.findActiveByTag(tag);返回ResponseEntity.ok () .body(拍卖);}
查询参数作为参数传递给使用@RequestParam注释的方法。
除了在拍卖中定义的核心类之外。在core包中,我还发现需要创建七个额外的类来满足API的特定需求。这些都是将被发布到服务器(通常用于实现远程过程调用)或作为对各种GET请求的响应从服务器返回的类。
这里有几个典型的例子。
当用户刚刚赢得拍卖时,第一个例子变得很重要。要获取描述他们赢得的拍卖的对象列表,用户将使用URL /users/
@GetMapping("/{id}/offers") public ResponseEntity<List<Offer>> getOffers(@PathVariable String id) {List<Offer> results = dao.findOffers(id);返回ResponseEntity.ok () .body(结果);}
此请求返回的Offer对象具有以下结构:
public class Offer {private String purchaseid;private String auctionid;私人的;私人int金额;}
这些Offer对象中的数据是数据库中purchase_detail视图中数据的子集。下面是从数据库中提取数据的DAO方法的代码:
public List<Offer> findOffers(String userId) {String sql = “select * from purchase_detail where投标人=? ”和状态= Won_bid”;购买列表< >购买;RowMapper<Purchase> RowMapper = new PurchaseRowMapper();尝试{购买= jdbcTemplate。查询(sql、rowMapper userId);} catch(Exception ex) {Purchase = new ArrayList<Purchase>();} ArrayList<Offer> result = new ArrayList<Offer>();for(Purchase p: purchases) {Offer 0 = new Offer(p);result.add (o);}返回结果;}
注意,这里使用的select语句最初是从数据库中提取Purchase对象列表。进入Offer对象的字段是这些字段的子集。为了将Purchase对象转换为Offer对象,我在Offer类中使用了一个特殊的构造函数,它从Purchase生成Offer:
public Offer(Purchase base) {purchaseid = base. getpurchaseid ();auctionid = base.getAuction();bidid = base.getBidid();amount = base.getBid();}
我对Sale和Bill类也使用了同样的技术。这三个类都实现了Purchase类的片段,以便向客户端传递更集中的信息。
另一组DTO类用于实现远程过程调用的参数。您可以在PurchaseController类中找到这些远程过程调用的许多示例。该类包含一系列方法,用于实现购买过程中的关键步骤。
这里有一个典型的例子。当用户执行如上所示的GET请求以获取他们赢得的拍卖的报价列表时,他们将被期望接受或拒绝每个报价。他们将通过一个远程过程调用来实现这一点,该过程调用涉及将一个OfferResponse对象发送到URL /purchases/ OfferResponse。下面是OfferResponse类的结构:
公共类OfferResponse{私有字符串purchaseid;private String userid;私有布尔接受;Private int shippingid;}
下面是PurchaseController中实现这个远程过程调用的方法:
@PostMapping("/offerresponse") public ResponseEntity<String> offerresponse(@RequestBody offerresponse or) {if(dao.saveOfferResponse(or)) return ResponseEntity.status(HttpStatus.CREATED)。身体(“提供接受”);else {dao. concludeuctions ();返回ResponseEntity.status (HttpStatus.ACCEPTED)。身体(“取消”);}}
PurchaseDAO类中在这里进行处理的方法也很有趣。它包含处理用户接受报价和拒绝报价两种情况的逻辑。
公共布尔saveOfferResponse(OfferResponse或){if(or. getaccept()){字符串sql = "更新购买设置shippingid=?, status='Confirmed' where purchaseid=?";jdbcTemplate.update (sql, or.getShippingid (), or.getPurchaseid ());返回true;} else{字符串searchPurchase = "select * from purchaseid=?";RowMapper<Purchase> RowMapper = new PurchaseRowMapper();购买结果;try {result = jdbcTemplate。queryForObject (searchPurchase rowMapper or.getPurchaseid ());} catch(Exception ex) {result = null;} if(result != null) {String deletePurchaseSQL = "delete from purchaseid=?";jdbcTemplate.update (deletePurchaseSQL);String deleteBidSQL = “从biddid =?的出价中删除”;jdbcTemplate.update (deleteBidSQL result.getBidid ());}返回false;}}
拒绝一个提议需要更复杂的过程。在这种情况下,我们必须查找购买的详细信息,从购买表中删除该购买,并找到导致生成此购买的中标,并将其从投标表中删除。