目录
开发之前的一些注意点domain设计:JdbcUtils设计dao层设计services层设计web层设计创建表的时候,id选用UUID,是为了以后合并表的时候,id不会冲突;(主要考虑是否有合并的可能性,没有合并的可能性,选择自增长的int)
table打开边框的时候,使用 frame="boder" 打开,这样即使没数据也会显示空的单元格;
删除文件的时候,将删除数据记录和删除硬盘文件放在一个事务里面;并且先删除数据库记录,后删除文件 ;假如先删除文件,后删除记录,再删除记录的时候,抛出异常,则数据库回滚,这时候文件已经被删除了,是不能被回滚的,就是操作数据库会出现异常,要保证异常发生时,2者统一;数据库建库 规定字符集为UTF-8:
CREATE DATABASE test CHARACTER SET utf8 COLLATE utf8_general_ci;建表 规定字符集为UTF-8 :
ENGINE=InnoDB DEFAULT CHARSET=utf8;从request中获取参数的时候,如果Javabean对应的属性,没有提交过来的参数。Beautil 会为对应的属性赋予默认值 ;
建表语句(实例):
最后加上规定字符集的语句 create table file ( uuid varchar(40) unique , simpleName varchar(20) not null, uploaderName varchar(10) not null, uploadDate date not null, description varchar(40) not null, savePath varchar(100) not null ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;对文件的描述:
File类 : 属性包括:上传者姓名,上传时间、文件名、文件UUID、文件保存路径、文件描述 ;
分页的设计
封装查询信息:
QueryInfo 对象 : 属性包括:页面数据量,查询的页码,数据库查询起始下标;
其中页面数据量和起始下标有个默认值,不指定就以该默认值为准;
public class QueryInfo { private int startIndex ; private int pageSize = 5 ; private int currentPage = 1 ; // 起始坐标,通过当前页和页面显示数据量计算得出 public int getStartIndex() { startIndex = (currentPage - 1) * pageSize ; return startIndex; } .... .... }QueryResult 对象 :属性包括:保存查询到的文件对象的集合、数据库数据总量 ;
PageBean 对象 :(属性就是页面要显示的数据)
1、当前页面(也就是QueryInfo的查询的页码),为了显示当前在第几页; 2、页面显示的数据量(就是QueryInfo里面的页码数据量), 为了显示当前页面显示多少数据量,也为了翻页的pageSize的属性自动赋值 ; 3、总页数(根据总条数除以页面显示数量,计算得到) // 计算总页数 public int getTotalPage() { totalPage = totalRecord % pageSize == 0 ? totalRecord / pageSize : totalRecord / pageSize + 1; return totalPage; } 4、页码条 先设置一个开始坐标和一个结束坐标;初始值都是0; 然后判断页码总长度是否大于我们设定的页码条的长度; 如果不大于,则页码条就是当前的页面数,无需做特殊处理; 如果大于,则开始坐标是当前页面坐标减去页码条长度的一半, 结束坐标是当前页面坐标加上页码条长度的一半; 然后判断,开始坐标是否小于1,如果小于,则令开始坐标为1,结束坐标为页码条的长度; 结束坐标是否大于总页面数,如果大于,则令结束坐标为页面总长度,开始坐标为页面总长度减去页码条长度; // 计算页码条 public int[] getPageBar() { int startIndex = 0; int endIndex = 0; // 默认显示 5 个页码条 if (getTotalPage() < 5) { pageBar = new int[getTotalPage()]; startIndex = 1; endIndex = getTotalPage(); } else { pageBar = new int[5]; startIndex = currentPage - 2; endIndex = currentPage + 2; // 判断是否越界 if (startIndex < 1) { startIndex = 1; endIndex = startIndex + 4; } if (endIndex > getTotalPage()) { endIndex = getTotalPage(); startIndex = getTotalPage() - 4; } } int index = 0; for (int i = startIndex; i <= endIndex; i++) { pageBar[index++] = i; } return pageBar; } 5、页面显示的文件对象集合 6、上一页、下一页 7、总记录数。(计算总页数需要用到)使用 ThreadLocal 类,实现事务;
getDataSource() :获取连接池 getConnecetion() : 获取连接 ,获取之前,先判断当前线程上是否有绑定的连接 ; stratTransaction() : 开启事务,先获取连接,然后绑定到当前线程上,然后开启事务 ; commitTranscation(): 提交事务,获取当前线程上的连接,然后提交事务 ; closeConnecetion() : 关闭连接,已经移除ThreadLocal里面的连接 ; public class JdbcUtils { private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); private static ComboPooledDataSource dataSource; static { try { // 没有参数,则使用默认的配置,创建数据库连接池 dataSource = new ComboPooledDataSource(); } catch (Exception e) { // 抛出初始化异常 throw new ExceptionInInitializerError(e); } } /** * 获取数据库连接池 * * @return */ public static DataSource getDataSource() { return dataSource; } /** * 获取连接,底层也是从数据库里面获取连接, * <p> * 判断当前线程上有没有连接,便于开启事务 * * @return * @throws SQLException */ public static Connection getConnection() { Connection connection = threadLocal.get(); try { if (connection == null) { connection = dataSource.getConnection(); } return connection; } catch (SQLException e) { throw new RuntimeException(e); } } /** * 为当前线程开启事务操作 */ public static void startTransaction() { Connection connection = threadLocal.get(); try { if (connection == null) { connection = dataSource.getConnection(); threadLocal.set(connection); } connection.setAutoCommit(false); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 提交事务 */ public static void commitTransaction(){ Connection connection = threadLocal.get() ; if (connection == null) { return; } try { connection.commit(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e) ; } } /** * 使用了连接池以后,不需要我们去关注连接的关闭问题了。 * * 但是使用了事务的连接,还是需要我们自己去关闭的 ; */ public static void closeConnection(){ Connection connection = threadLocal.get() ; try { if (connection != null) { connection.close(); } }catch (SQLException e){ e.printStackTrace(); throw new RuntimeException(e) ; }finally { threadLocal.remove(); } } }将request中的数据封装到Javabean中;
使用Beanutils; 先将request中的参数,封装到map集合中:request.getParameterMap() ; 再调用Beanutils的方法(BeanUtils.populate(bean,map)),将map中的元素,封装到Javabean中; 注意,如果request没有对应的属性,则属性被赋予默认值;ShowUtils
将request中的查询信息封装到QueryInfo中,便于分页;,写一个工具类,就避免了我们为QueryInfo对象的每一个属性,一一赋值; 然后通过services获取到PageBean,传给显示的JSP;将上传的文件保存到服务器硬盘上,并且返回一个bean对象;
首先通过文件的item,获取文件的名字,如果为空串,则表示没有上传文件; 检查文件后缀名是否允许上传;合法,就将其保存到文件对象的属性中,不合法则抛出自定义的后缀名异常; 然后通过文件名字,进行分配文件保存路径;还是保存到文件对象的Javabean上; 获取UUID、日期,都保存到文件对象的Javabean上; 最后将文件写到服务器硬盘上,注意的是,数据库里面仅仅保存文件保存的路径,而不真正的保存文件; 写文件,通过文件的item,获取输入流,然后new FileOutputStream 参数是 路径_文件名 ; 跟java中IO一样,这里参数写上绝对路径,假如写上相对路径, 在JSP、servlet中写上相对路径,相对的是tomcat的bin路径,因为这是在J2E项目中;计算文件分配路径
通过文件名的hashcode,分配文件路径;跟之前讲的分配算法是一样的思路; /** * 最多可以保存8层,这样的算法,这里仅取两层 * * @param name * @return WEB-INF/upload/5/6 */ public static String getPath(String path, String name) { int hashcode = name.hashCode(); int one = hashcode & 0xf; int two = (hashcode >> 4) & 0xf; File file = new File(path + File.separator + one + File.separator + two); if (!file.exists()) { file.mkdirs(); } return file.getPath(); }