WEB 小案例 -- 网上书城(一)
距离上次写博客有两周了吧,最多的原因就是自己期末考试了,上课没听就只能在期末狠狠的复习了,毕竟已经挂科了。当然还是因为自己懒吧!!!废话不多说开始我们今天的正题,网上书城!
一、 新建数据表(MySQL 数据库)
对于网上书城其后台应该有存放商品即书籍信息的数据表(books),存放用户信息的数据表(userInfo),存放用户账户信息的数据表(account),存放交易记录的数据表(trade),存放对应交易记录所交易商品的信息的数据表(tradeItem)。其 SQL 语句如下:
1. account 数据表:
1 CREATE TABLE `account` ( 2 `accountid` int(11) NOT NULL auto_increment, 3 `balance` float default NULL, 4 PRIMARY KEY (`accountid`) 5 ) 6 7 insert into `account`(`accountid`,`balance`) values (1,9740);
2. books 数据表:
1 CREATE TABLE `books` ( 2 `Id` int(11) NOT NULL auto_increment, 3 `Author` varchar(30) NOT NULL, 4 `Title` varchar(30) NOT NULL, 5 `Price` float NOT NULL, 6 `Publishingdate` date NOT NULL, 7 `Salesamount` int(11) NOT NULL, 8 `Storenumber` int(11) NOT NULL, 9 `Remark` text NOT NULL, 10 PRIMARY KEY (`Id`) 11 ) 12 13 14 insert into `books`(`Id`,`Author`,`Title`,`Price`,`Publishingdate`,`Salesamount`,`Storenumber`,`Remark`) values (1,'Tom','Java 编程思想',50,'2009-06-22',17,83,'Good Java Book'),(2,'Jerry','Oracle DBA 教材',60,'2009-06-22',12,88,'Good Oracle Book'),(3,'Bob','Ruby',50,'2009-06-22',12,88,'Good 0'),(4,'Mike','Javascript',51,'2009-06-22',0,100,'Good 1'),(5,'Rose','Ajax',52,'2009-06-22',0,100,'Good 2'),(6,'Backham','Struts',53,'2009-06-22',0,100,'Good 3'),(7,'Zidon','Hibernate',54,'2009-06-22',2,12,'Good 4'),(8,'Ronaerdo','Spring',55,'2009-06-22',2,13,'Good 5'),(9,'Clinsman','Cvs',56,'2009-06-22',0,16,'Good 6'),(10,'Kaka','Seo',57,'2009-06-22',0,17,'Good 7'),(11,'Lauer','Lucence',58,'2009-06-22',0,18,'Good 8'),(12,'Kasi','Guice',59,'2009-06-22',0,19,'Good 9'),(13,'Prierlo','Mysql',60,'2009-06-22',6,14,'Good 10'),(14,'Haohaidong','DB2',61,'2009-06-22',9,12,'Good 11'),(15,'Feige','Sybase',62,'2009-06-22',8,14,'Good 12'),(16,'Tuoleisi','DBDesign',63,'2009-06-22',0,23,'Good 13'),(17,'Jielade','Eclipse',64,'2009-06-22',0,24,'Good 14'),(18,'Teli','Netbeans',65,'2009-06-22',0,25,'Good 15'),(19,'Lapade','C#',66,'2009-06-22',0,26,'Good 16'),(20,'Runi','JDBC',67,'2009-06-22',0,27,'Good 17'),(21,'JoeKeer','Php',68,'2009-06-22',0,28,'Good 18'),(22,'Jordan','MysqlFront',69,'2009-06-22',5,24,'Good 19'),(23,'Yaoming','NoteBook',70,'2009-06-22',5,25,'Good 20'),(24,'Yi','C',71,'2009-06-22',5,26,'Good 21'),(25,'Sun','Css',72,'2009-06-22',0,32,'Good 22'),(26,'Xuliang','JQuery',73,'2009-06-22',0,33,'Good 23'),(27,'Meixi','Ext',74,'2009-06-22',0,34,'Good 24'),(28,'Apple','iphone',75,'2009-06-22',0,35,'Good 25'),(29,'Aigo','dc',76,'2009-06-22',0,36,'Good 26'),(30,'Sony','psp',77,'2009-06-22',0,100,'Good 27'),(31,'IRiver','mp3',78,'2009-06-22',0,100,'Good 28'),(32,'Sansing','TV',79,'2009-06-22',0,100,'Good 29');
3. trade 数据表:
1 CREATE TABLE `trade` ( 2 `tradeid` int(11) NOT NULL auto_increment, 3 `userid` int(11) NOT NULL, 4 `tradetime` datetime NOT NULL, 5 PRIMARY KEY (`tradeid`), 6 KEY `user_id_fk` (`userid`), 7 CONSTRAINT `user_id_fk` FOREIGN KEY (`userid`) REFERENCES `userinfo` (`userid`) 8 )
4. tradeItem 数据表:
1 CREATE TABLE `tradeitem` ( 2 `itemid` int(11) NOT NULL auto_increment, 3 `bookid` int(11) default NULL, 4 `quantity` int(11) default NULL, 5 `tradeid` int(11) default NULL, 6 PRIMARY KEY (`itemid`), 7 KEY `book_id_fk` (`bookid`), 8 KEY `trade_id_fk` (`tradeid`), 9 CONSTRAINT `book_id_fk` FOREIGN KEY (`bookid`) REFERENCES `mybooks` (`Id`), 10 CONSTRAINT `trade_id_fk` FOREIGN KEY (`tradeid`) REFERENCES `trade` (`tradeid`) 11 )
5. userInfo 数据表:
1 CREATE TABLE `userinfo` ( 2 `userid` int(11) NOT NULL auto_increment, 3 `username` varchar(50) default NULL, 4 `accountid` int(11) default NULL, 5 PRIMARY KEY (`userid`), 6 KEY `account_id_fk` (`accountid`), 7 CONSTRAINT `account_id_fk` FOREIGN KEY (`accountid`) REFERENCES `account` (`accountid`) 8 ) 9 10 insert into `userinfo`(`userid`,`username`,`accountid`) values (1,'yinyin',1);
6. shoppingCart 数据表:
1 CREATE TABLE `shoppingcart` ( 2 `cart_id` int(11) NOT NULL auto_increment, 3 `user_id` int(11), 4 `cart_count` int(11), 5 `cart_name` varchar(50), 6 `cart_price` int(11), 7 PRIMARY KEY (`cart_id`), 8 KEY `user_id_fk` (`user_id`), 9 CONSTRAINT `user_id_fk` FOREIGN KEY (`user_id`) REFERENCES `userinfo` (`accountid`) 10 )
二、 Domain 类以及 DAO 接口编写
我们已经将底层数据库编写完成,现在我们要以数据表为基础结合我们要实现的功能去设计 DAO 接口,以及根据数据表编写 Domain 类即一般的 java 类。Domain 类如下:
1. 结构
2. 我们结合数据表编写 Domain 类时需要清楚每一张表的功能,这样思路才可以更清晰
① Account
1 public class Account { 2 private int account_id; 3 private int balance; 4 5 public Account(int balance) { 6 this.balance = balance; 7 } 8 9 public Account() {} 10 11 @Override 12 public String toString() { 13 return "Account{" + 14 "account_id=" + account_id + 15 ", balance=" + balance + 16 '}'; 17 } 18 19 public int getAccount_id() { 20 return account_id; 21 } 22 23 public void setAccount_id(int account_id) { 24 this.account_id = account_id; 25 } 26 27 public int getBalance() { 28 return balance; 29 } 30 31 public void setBalance(int balance) { 32 this.balance = balance; 33 } 34 }
② Books
1 import java.sql.Date; 2 3 /** 4 * 依据数据表 Books 新建实体类 books 5 */ 6 public class Books { 7 private Integer id; 8 private String author; 9 private String title; 10 private Integer price; 11 private Date publishDate; 12 private Integer salesCount; 13 private Integer storeNumber; 14 private String remark; 15 16 public Integer getId() { 17 return id; 18 } 19 20 public void setId(Integer id) { 21 this.id = id; 22 } 23 24 public String getAuthor() { 25 return author; 26 } 27 28 public void setAuthor(String author) { 29 this.author = author; 30 } 31 32 public String getTitle() { 33 return title; 34 } 35 36 public void setTitle(String title) { 37 this.title = title; 38 } 39 40 public Integer getPrice() { 41 return price; 42 } 43 44 public void setPrice(Integer price) { 45 this.price = price; 46 } 47 48 public Date getPublishDate() { 49 return publishDate; 50 } 51 52 public void setPublishDate(Date publishDate) { 53 this.publishDate = publishDate; 54 } 55 56 public Integer getSalesCount() { 57 return salesCount; 58 } 59 60 public void setSalesCount(Integer salesCount) { 61 this.salesCount = salesCount; 62 } 63 64 public Integer getStoreNumber() { 65 return storeNumber; 66 } 67 68 public void setStoreNumber(Integer storeNumber) { 69 this.storeNumber = storeNumber; 70 } 71 72 public String getRemark() { 73 return remark; 74 } 75 76 public void setRemark(String remark) { 77 this.remark = remark; 78 } 79 80 public Books() { 81 82 } 83 84 public Books(Integer id, String author, String title, Integer price, Date publishDate, Integer salesCount, Integer storeNumber, String remark) { 85 this.id = id; 86 this.author = author; 87 this.title = title; 88 this.price = price; 89 this.publishDate = publishDate; 90 this.salesCount = salesCount; 91 this.storeNumber = storeNumber; 92 this.remark = remark; 93 } 94 95 public Books(String title, Integer price) { 96 this.title = title; 97 this.price = price; 98 } 99 100 @Override 101 public String toString() { 102 return "Books{" + 103 "id=" + id + 104 ", author='" + author + '\'' + 105 ", title='" + title + '\'' + 106 ", price=" + price + 107 ", publishDate=" + publishDate + 108 ", salesCount=" + salesCount + 109 ", storeNumber=" + storeNumber + 110 ", remark='" + remark + '\'' + 111 '}'; 112 } 113 }
③ ShoppingCartItems ④ Trade ⑤ TradeItem ⑥ UserInfo
其他的 Domain 类和前面两个一样,都是根据数据表对应的列添加对应类型的变量,至于 Damain 类对应的 Dao 接口及其实现类我们后面根据具体功能编写添加。
三、 JDBC 实现
1. 连接数据库的工具类实现 JDBCTools.java 以及配置文件 c3p0-config.xml
1 package com.book.store.db; 2 3 import com.mchange.v2.c3p0.ComboPooledDataSource; 4 5 import javax.sql.DataSource; 6 import java.sql.Connection; 7 import java.sql.SQLException; 8 9 /** 10 * 数据库工具类 11 */ 12 public class JDBCTools { 13 // 定义数据源 14 private static DataSource dataSource; 15 // 初始化数据源,静态代码块只会执行一次,提高了效率 16 static { 17 dataSource = new ComboPooledDataSource("bookStore"); 18 } 19 /* 20 * 获取数据库连接 21 * */ 22 public static Connection getConnection() { 23 Connection connection = null; 24 try { 25 // 从数据源总获取数据库连接 26 connection = dataSource.getConnection(); 27 } catch (SQLException e) { 28 e.printStackTrace(); 29 } 30 return connection; 31 } 32 /* 33 * 释放数据库连接 34 * */ 35 public static void releaseConnection(Connection connection) { 36 try { 37 connection.close(); 38 } catch (SQLException e) { 39 e.printStackTrace(); 40 } 41 } 42 }
c3p0-config.xml
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <c3p0-config> 3 <named-config name="bookStore"> 4 <property name="user">root</property> 5 <property name="password">zy961029</property> 6 <property name="driverClass">com.mysql.jdbc.Driver</property> 7 <property name="jdbcUrl">jdbc:mysql:///bookshop</property> 8 </named-config> 9 </c3p0-config>
2. 增删改查操作类的设计(Dao.java)以及实现 (DaoImpl.java)
1 package com.book.store.dao; 2 3 import java.util.List; 4 5 /** 6 * 数据库基本操作 7 */ 8 public interface Dao<T> { 9 // 执行插入操作 10 long insert(String sql, Object ... args); 11 // 执行更新操作 12 void update(String sql, Object ... args); 13 // 查找对象集合 List 14 List<T> getList(String sql, Object ... args); 15 // 查找对象 16 T getValue(String sql, Object ... args); 17 // 查找单个值 18 Object getCount(String sql, Object ... args); 19 // 执行批量更新操作, 20 void batch(String sql, Object[] ... params); 21 }
DaoImpl.java(在这里我们使用 ThreadLocal 实现处理事务)
1 package com.book.store.impl; 2 3 import com.book.store.dao.Dao; 4 import com.book.store.db.JDBCTools; 5 import com.book.store.web.ThreadLocalConnection; 6 import org.apache.commons.dbutils.QueryRunner; 7 import org.apache.commons.dbutils.handlers.BeanHandler; 8 import org.apache.commons.dbutils.handlers.BeanListHandler; 9 import org.apache.commons.dbutils.handlers.ScalarHandler; 10 11 import java.lang.reflect.ParameterizedType; 12 import java.lang.reflect.Type; 13 import java.sql.*; 14 import java.util.List; 15 16 /** 17 * 使用 DBUtils 和 C3p0 实现操作数据库的基本方法 18 */ 19 public class DaoImpl<T> implements Dao<T> { 20 QueryRunner queryRunner; 21 Class<T> type; 22 // 使用反射动态创建实体类的 class 23 public DaoImpl() { 24 queryRunner = new QueryRunner(); 25 Type superClass = getClass().getGenericSuperclass(); 26 27 if (superClass instanceof ParameterizedType) { 28 ParameterizedType parameterizedType = (ParameterizedType) superClass; 29 30 Type[] args = parameterizedType.getActualTypeArguments(); 31 if (args != null && args.length > 0) { 32 if (args[0] instanceof Class) { 33 type = (Class<T>) args[0]; 34 } 35 } 36 } 37 } 38 /* 39 * 我们在后面往表中插入数据后需要根据自动生成的主键值去操作其他数据表,所以我们这里使用原生 JDBC 代码去插入数据,并返回自动生成的主键值 40 * */ 41 @Override 42 public long insert(String sql, Object... args) { 43 long id = 0; 44 // 获取过滤器中绑定的 connection 对象 45 Connection connection = ThreadLocalConnection.getInstance().get(); 46 // 执行语句后的对象 47 ResultSet resultSet; 48 try { 49 // 获取 prepareStatement 对象,并设置返回自动生成的主键 50 PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); 51 // 给参数赋值 52 for (int i = 0; i < args.length; i++) { 53 preparedStatement.setObject(i + 1, args[i]); 54 } 55 // 执行插入操作 56 preparedStatement.executeUpdate(); 57 // 将返回结果赋值给 resultSet 58 resultSet = preparedStatement.getGeneratedKeys(); 59 if (resultSet.next()) { 60 // 将结果赋值给 id 61 id = resultSet.getLong(1); 62 } 63 } catch (SQLException e) { 64 e.printStackTrace(); 65 } 66 return id; 67 } 68 /* 69 * 使用 DBUtils 更新操作 70 * */ 71 @Override 72 public void update(String sql, Object... args) { 73 Connection connection = ThreadLocalConnection.getInstance().get(); 74 75 try { 76 queryRunner.update(connection, sql, args); 77 } catch (SQLException e) { 78 e.printStackTrace(); 79 } 80 } 81 /* 82 * 使用 DBUtils 获取对应对象的 list 集合 83 * */ 84 @Override 85 public List<T> getList(String sql, Object... args) { 86 Connection connection = ThreadLocalConnection.getInstance().get(); 87 List<T> list = null; 88 89 try { 90 list = queryRunner.query(connection, sql, new BeanListHandler<T>(type), args); 91 } catch (SQLException e) { 92 e.printStackTrace(); 93 } 94 return list; 95 } 96 /* 97 * 使用 DBUtils 获取某一个对象 98 * */ 99 @Override 100 public T getValue(String sql, Object... args) { 101 Connection connection = ThreadLocalConnection.getInstance().get(); 102 T entity = null; 103 104 try { 105 entity = queryRunner.query(connection, sql, new BeanHandler<T>(type), args); 106 } catch (SQLException e) { 107 e.printStackTrace(); 108 } 109 110 return entity; 111 } 112 /* 113 * 执行批量更新操作(循环实现):完成交易的时候需要将此次交易信息批量插入数据表中,以及批量更新商品数据表的库存量和销售量等操作 114 * */ 115 @Override 116 public void batch(String sql, Object[]... params) { 117 Connection connection = ThreadLocalConnection.getInstance().get(); 118 119 try { 120 for (int i = 0; i < params.length; i++) { 121 queryRunner.update(connection, sql, params[i]); 122 } 123 } catch (SQLException e) { 124 e.printStackTrace(); 125 } 126 } 127 /* 128 * 获取某一个值:根据 Id 获取其他信息等 129 * */ 130 @Override 131 public Object getCount(String sql, Object... args) { 132 Object totalNum = null; 133 Connection connection = ThreadLocalConnection.getInstance().get(); 134 135 try { 136 totalNum = queryRunner.query(connection, sql, new ScalarHandler(), args); 137 } catch (SQLException e) { 138 e.printStackTrace(); 139 } 140 return totalNum; 141 } 142 }
ThreadLocalConnection.java (事务操作的基类,单例实现使得结账操作中的多个操作使用一个数据库连接,从而完成事务操作)
1 package com.book.store.web; 2 3 import java.sql.Connection; 4 5 /** 6 * 封装处理事务的方法 7 */ 8 public class ThreadLocalConnection { 9 10 private ThreadLocalConnection(){} 11 12 private static ThreadLocalConnection instance = new ThreadLocalConnection(); 13 14 public static ThreadLocalConnection getInstance() { 15 return instance; 16 } 17 18 private ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<Connection>(); 19 20 public void bind(Connection connection) { 21 connectionThreadLocal.set(connection); 22 } 23 24 public Connection get() { 25 return connectionThreadLocal.get(); 26 } 27 28 public void remove() { 29 connectionThreadLocal.remove(); 30 } 31 }
使用 ThreadLocal 处理事务,即通过 ThreadLocal.set() 将对象的引用保存到各线程的自己的一个 map 中,每个线程都有这样一个 map,执行 ThreadLocal.get() 时,各线程从自己的 map 中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal 实例是作为 map 的 key 来使用的,这样便可以在最后的结账操作事务中合法完成。
一般情况下,通过 ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程不需要访问,所以说 ThreadLocal 不能解决共享对象的多线程访问问题。
这次我们就先把有关数据库操作讲解完,后面我们接着这次继续讲解。如果有什么表述错误的地方欢迎大家指点,谢谢!!!