总结:java小项目——图书管理系统
一、用到的工具
1.eclipse+windowbuilder插件
二、涉及的知识点
1. MVC设计模式
2. Swing
3. JDBC+MySQL
三、框架搭建
按照MVC设计模式在eclipse中新建项目,导入项目中要用到的图标,在数据库中创建表(以及表之间的主外键关联),用JDBC知识成功连接数据库。
【JAVA%20MVC】
即把一个应用的输入、处理、输出流程按照Model、View、Controller的方式进行分离,这样一个应用被分成三个层——模型层、视图层、控制层。
M——Model(模型)。业务流程/状态的处理以及业务规则的制定。把抽象的概念化成一个个类,例如User、Book、BookType。
V——View(视图)。视图接收来自Model的数据并显示给用户,以及将用户界面的输入数据和请求传递给Controller和Model。此部分用windowbuilder插件来实现。
C——Controller(控制器)。这部分主要是用来连接Model和View这两部分,控制层收到请求后,%20并不处理业务信息,它只把用户的信息传递给相应的Model,告诉模型做什么,选择符合要求的View返回给用户。关于用户交互的操作的方法函数写在这一部分。
——包com.BookManager.dao用来写Controller这一模块;
——包com.BookManager.model用来写Model这一模块;
——包com.BookManager.view用来写View这一模块;
另外,将多次使用的工具类都写在com.BookManager.util包中,如数据库连接、判断字符串是否为空。
【包images】
下载好图标后,新建一个images包,将复制好的图标直接ctrl+v粘贴进来即可。包images用来存放项目中用到的图标和图片(http://www.easyicon.net/ )
【数据库创建表】
1.【varchar】数据库创建表时,对于userName等项应设置成varchar数据类型,char是定长的字符,varchar[n]存储大小为输入数据字节的实际长度,而不是%20n%20个字节。
2.【主外键关联】
主键的主要作用是将记录和存放在其他表中的数据进行关联,在这一点上,主键是不同表中各记录间的简单指针,不能有重复的,不允许为空。
外键是另一表的主键,可以有重复,可以是空值,用来和其他表建立联系用的。所以说,如果谈到了外键,一定至少涉及两张表。外键约束主要用来维护两个表之间数据的一致性。
在SQLyog软件中,点击“架构设计器”拖动要关联的两张表,如下图所示。
【JDBC】
1.【数据库中创建表】t_User,设置用户名及密码。
2.【驱动】下载MySQL相应的驱动包,新建jdbc文件夹,将包复制粘贴到此文件夹中,然后Build%20Path—>Addto%20Build%20Path,就将此包添加到项目中了。
3.【按MySQL格式写代码】封装DbUtil类。代码中按下快捷键ctrl+shift+o要导入sql的包时,应选择jdbc的接口——java.sql.Connection,如下图所示。
四、代码总结
<一>Model模块
在Model中将抽象概念Book、BookType、User用代码变量描述出来,即创建实体的描述。
Alt+Shift+s弹出自动生成语句的菜单(创建成员变量get()和set()方法)。
用到包装类,以便能将基本类型当作对象处理。
【特别注意】构造方法中形参的每一项(顺序)都要与数据库中相应表中的栏目名称和类型保持一致。
此外,BooType类中重写了toString()的方法。因为把BookType类的对象当作参数传进去后,显示出来的并不是它里面的数据,而是它的地址。所以重写toString()方法来显示BookType类的对象里面的数据。
<二>Controller模块
连接用户输入的数据和数据库里面的数据的操作。在其他类中用到这些类的方法时,要先new相应的对象。
1.用户登录——UserDao.java
实现用户登录数据库功能,即输入用户名和密码,如果数据库的t_user表中含有匹配的用户名和密码,就能登录成功。所以,此方法应该为User类型,传入参数为数据库连接和用户——public User login(Connection con, Useruser)throws Exception{}
·先定义变量sql,赋予MySQL的原始语句;
·调用PRepareStatement()来预处理sql;
·调用setString(),设置MySQL语句中占位符的内容;
·调用executeQuery();返回ResultSet结果集。【注意】调用此方法后,已经执行了sql语句的查询功能,即比对查询数据库中是否有将要输入(占位符)的数据。而数据的接收(输入数据)是在View模块中实现的,两个模块的变量相互呼应。
·判断查询数据库的结果集是否含有输入的记录if(rs.next()),如果有,则实例化用户对象,并对其记录进行设置。
public class UserDao { public User login(Connection con,User user)throws Exception{ User resultUser = null; /** * PreparedStatement接口是Statementd的子接口,用于预编译SQL语句。 * 预编译后的SQL语句被存储在PreparedStatement对象中, * 然后可以使用该对象多次高效率地执行该语句(比Statement的效率高)。 * * Statement执行SQL语句时不允许使用问号占位符参数 * PreparedStatement执行SQL语句时可以使用占位符,执行SQL语句之前必须为这些参数传入参数值 * * PreparedStatement也提供了execute()、executeUpdate()、executeQuery()三个方法执行SQL语句, * 不过他们无需参数,因为PreparedStatement已经存储了预编译的SQL语句 */ String sql = "select*from t_user where userName=? and passWord=?"; PreparedStatement pstmt = con.prepareStatement(sql); pstmt.setString(1, user.getUserName()); pstmt.setString(2, user.getPassword()); ResultSet rs = pstmt.executeQuery(); /** * 【注意】调用此方法后,已经执行了sql语句的查询功能, * 即比对查询数据库中是否有将要输入(占位符)的数据。 * 而数据的接收(输入数据)是在View模块中实现的, * 两个模块的变量相互呼应。 */ if(rs.next()){ resultUser = new User(); resultUser.setId(rs.getInt("id")); resultUser.setUserName(rs.getString("userName")); resultUser.setPassword(rs.getString("password")); } return resultUser; }}2.图书类别操作——BookTypeDao.java
实现图书类别的添加、删除、查询显示和修改维护以及判断此类别是否含有图书。
【SQL语句拼接】因为是动态查询,bookTypeName可能没有值,因此涉及到判断是否为空,所以要用“拼接”的方式来写SQL语句——
·用StringBuffer暂存一下字符串;
·符合判断条件时,用append拼接,拼接的SQL语句用and连接,之后再替换成where(因为在拼接的两段SQL语句中,where的位置可能会造成多个if时的混乱,所以采用替换的方式来处理);
·用toString()将StringBuffer中的内容转换成字符串,再将字符串内容的第一个and替换成where,即正式的SQL语句。
public ResultSet list(Connection con,BookType bookType) throws Exception{ StringBuffer sb = new StringBuffer("select*from t_bookType ");//用StringBuffer暂存一下字符串 if(StringUtil.isNotEmpty(bookType.getBookTypeName())){ //因为是动态查询,bookTypeName可能没有值,所以要用“拼接”的方式来写SQL语句 sb.append(" and bookTypeName like '%" +bookType.getBookTypeName()+"%'"); } //先用toString()将StringBuffer中的内容转换成字符串,再将字符串内容的第一个and替换成where,即正式的SQL语句 //在拼接的两段SQL语句中,where的位置可能会造成多个if时的混乱,所以采用替换的方式来处理 PreparedStatement pstmt = con.prepareStatement(sb.toString().replaceFirst("and", "where")); return pstmt.executeQuery(); }3.图书操作——BookDao.java
实现图书的添加、查询、删除、修改。
两个表的关联查询。
public class BookDao { /** * 图书信息添加 * @param con * @param book * @return * @throws Exception */ public int add(Connection con , Book book)throws Exception{ String sql = "insert into t_book values(null,?,?,?,?,?,?)"; PreparedStatement pstmt = con.prepareStatement(sql); pstmt.setString(1, book.getBookName()); /** * 【特别注意】对每一个"?"进行设置时都要与数据库中t_book表中的栏目名称和类型保持一致. * 比如: * 如果原t_book表中第6栏是price,则第六个"?"处就代表price,类型就是float, * 那么若执行pstmt.setString(6, book.getBookDesc());就会因为名称和类型不一致而报错 */ pstmt.setString(2, book.getAuthor()); pstmt.setString(3, book.getSex()); pstmt.setInt(4, book.getBookTypeId()); pstmt.setString(5, book.getBookDesc()); pstmt.setFloat(6, book.getPrice()); return pstmt.executeUpdate();//为什么不能方法返回值为void,并把return去掉—————— //executUpdate()本身返回int类型,返回受影响的记录条数。 //也便于后面根据返回值而进一步判断执行 } /** * 图书信息查询 * @param con * @param book * @return * @throws Exception */ public ResultSet list(Connection con ,Book book)throws Exception{ //t_book表中的id关联到了t_bookType表的bookTypeId,所以要执行两个表的关联查询 //t_book表的外键等于t_bookType表的主键 StringBuffer sb = new StringBuffer("select * from t_book b,t_bookType bt where b.bookTypeId=bt.id"); if(StringUtil.isNotEmpty(book.getBookName())){ sb.append(" and b.bookName like '%"+book.getBookName()+"%'");//like模糊查询 } if(StringUtil.isNotEmpty(book.getAuthor())){ sb.append(" and b.author like '%"+book.getAuthor()+"%'");// '% "关键词" %' 比如 '%java%' } //此时才表示用户选中了图书类别。(类别选择框中“请选择”的ID在BookManageInterFrm中已设为-1) if(book.getBookTypeId()!=null&&book.getBookTypeId()!=-1){ sb.append(" and b.bookTypeId="+book.getBookTypeId()); }// PreparedStatement pstmt = con.prepareStatement(sb.toString().replaceFirst("and", "where")); PreparedStatement pstmt = con.prepareStatement(sb.toString());//只能有一个where,所以不需要再把and替换成where return pstmt.executeQuery(); } /** * 图书信息删除 * @param con * @param id * @return * @throws Exception */ public int delete(Connection con ,String id)throws Exception{ String sql = "delete from t_book where id=?"; PreparedStatement pstmt = con.prepareStatement(sql); pstmt.setString(1, id); return pstmt.executeUpdate(); } /** * 图书信息修改 * @param con * @param book * @return * @throws Exception */ public int update(Connection con,Book book)throws Exception{ //要注意此处的顺序要与数据库中每一项的顺序严格一致! String sql = "update t_book set bookName=?,author=?,sex=?,bookTypeId=?,bookDesc=?,price=?where id=?"; PreparedStatement pstmt = con.prepareStatement(sql); pstmt.setString(1, book.getBookName()); pstmt.setString(2, book.getAuthor()); pstmt.setString(3, book.getSex()); pstmt.setInt(4, book.getBookTypeId()); pstmt.setString(5, book.getBookDesc()); pstmt.setFloat(6, book.getPrice()); pstmt.setInt(7, book.getId());//要注意sql语句中要有"where id=?" return pstmt.executeUpdate(); } }<三>View模块
借助windowbuilder插件,新建类时new—>other—>windowbuilder
记得将窗体部件重命名,以便代码中当作对象调用方法。并且相关部件要在开头声明(有些自动生成的代码没有在开头申明需要手动调整完善)
【表格显示查询结果】拖入scrollPane,然后在它里面拖入Jtable,通过Jtable的model属性设置表的标题与行列数,注意一定要与数据库中的顺序类型保持一致。
/** * 显示结果表单 * @param book */ private void fillTable(Book book){ DefaultTableModel dtm = (DefaultTableModel) bookTable.getModel();//提前将table组件改名 dtm.setRowCount(0);//清空表单 Connection con = null; try{ con = dbUtil.getCon(); ResultSet rs = bookDao.list(con, book); while(rs.next()){ Vector v = new Vector(); v.add(rs.getInt("id")); v.add(rs.getString("bookName")); v.add(rs.getString("author")); v.add(rs.getString("sex")); v.add(rs.getString("bookTypeName"));//与数据库的顺序、名称保持一致 v.add(rs.getString("bookDesc")); v.add(rs.getFloat("price")); dtm.addRow(v); } }catch(Exception e){ e.printStackTrace(); }finally{ try { dbUtil.closeCon(con); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }【初始化下拉框】
/** * 初始化下拉框 * @param type */ private void fillBookType(String type){ Connection con = null; try{ con = dbUtil.getCon(); ResultSet rs = bookTypeDao.list(con, new BookType()); //在选择下拉框里添加未选择时的"请选择" if("search".equals(type)){ BookType bookType = new BookType(); bookType.setBookTypeName("请选择"); bookType.setId(-1);//设置新添加的"请选择"的ID为-1 this.s_bookTypeJcb.addItem(bookType);//将此项添加到下拉框中 } while(rs.next()){ BookType bookType = new BookType(); bookType.setBookTypeName(rs.getString("bookTypeName")); bookType.setId(rs.getInt("id")); if("search".equals(type)){ this.s_bookTypeJcb.addItem(bookType); }else if("modify".equals(type)){ this.bookTypeJcb.addItem(bookType); } } }catch(Exception e){ try { dbUtil.closeCon(con); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } }【JRadioButton】要执行右键,setButtonGroup将两个JRadioButton按钮加入到一个组中,才能实现二选一(非此即彼)。
【表格点击对应显示】
/** * 表格行点击事件处理 * 将选中的行的信息分别显示到面板中相应的项里 * @param met */ private void bookTableMousePressed(MouseEvent met) { int row = this.bookTable.getSelectedRow();//获取的是行号 //要与数据库中的各项顺序一致 this.idTXT.setText((Integer) bookTable.getValueAt(row, 0)+"");//【注意】id在fillTable方法中是getInt类型 //所以此处应为Integer类型,但是setText方法里面是String类型 //采取的办法就是【(Integer)+""】形式来转成String。否则报错。 //下面Float同理 this.bookNameTXT.setText((String) bookTable.getValueAt(row, 1)); this.authorTXT.setText((String) bookTable.getValueAt(row, 2)); //"性别"是获取后在选项前勾选的,不能直接设置文本显示 String sex = (String) bookTable.getValueAt(row, 3); if("男".equals(sex)){ this.manJrb.setSelected(true); } if("女".equals(sex)){ this.femaleJrb.setSelected(true); } //"图书类别"是下拉框显示,也不能直接设置文本显示 String bookTypeName = (String) bookTable.getValueAt(row, 4); int n = bookTypeJcb.getItemCount();//下拉框bookTypeJcb中有n个项 for(int i=0;i<n;i++){//遍历n个项,如果其中第i项(item)的名称与点击行的bookTypeName相同,则下拉框显示此i项 BookType item = (BookType) bookTypeJcb.getItemAt(i);//【注意】此处获取的是图书类别对象,不是图书类别名称 if(item.getBookTypeName().equals(bookTypeName)){ this.bookTypeJcb.setSelectedIndex(i);//下拉框显示此i项 } } this.bookDescTXT.setText((String) bookTable.getValueAt(row, 5)); this.priceTXT.setText((Float) bookTable.getValueAt(row, 6)+""); }【图书信息修改】/** * 图书修改事件处理 * @param evt */ private void bookUpdateActionPerformed(ActionEvent evt) { String id = idTXT.getText();//因为id是int类型,所以创建对象时传入参数要进行类型转换Integer.parseInt(id) if(StringUtil.isEmpty(id)){ JOptionPane.showMessageDialog(null, "请选择要修改的图书"); return; } String bookName = this.bookNameTXT.getText(); String bookDesc = this.bookDescTXT.getText(); String author = this.authorTXT.getText(); String price = this.priceTXT.getText(); if(StringUtil.isEmpty(bookName)){ JOptionPane.showMessageDialog(null, "图书名称不能为空"); return;//不能少了return } if(StringUtil.isEmpty(author)){ JOptionPane.showMessageDialog(null, "图书作者不能为空"); return;//不能少了return } if(StringUtil.isEmpty(price)){ JOptionPane.showMessageDialog(null, "图书价格不能为空"); return;//不能少了return } String sex = ""; if(manJrb.isSelected()) sex = "男"; if(femaleJrb.isSelected()) sex = "女"; BookType bookType = (BookType) bookTypeJcb.getSelectedItem(); int bookTypeId = bookType.getId(); Book book = new Book( Integer.parseInt(id), bookName, author, sex, bookTypeId, bookDesc, Float.parseFloat(price) ); Connection con = null; try{ con = dbUtil.getCon(); int updateNum = bookDao.update(con, book); if(updateNum==1){ JOptionPane.showMessageDialog(null, "图书修改成功"); resetValue(); this.fillTable(new Book());//实时修改刷新表单 }else{ JOptionPane.showMessageDialog(null, "图书修改失败"); } }catch(Exception e){ e.printStackTrace(); JOptionPane.showMessageDialog(null, "图书修改失败"); }finally{ try { dbUtil.closeCon(con); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } private void resetValue() { this.idTXT.setText(""); this.bookNameTXT.setText(""); this.authorTXT.setText(""); this.priceTXT.setText(""); this.bookDescTXT.setText(""); this.manJrb.setSelected(true); if(this.bookTypeJcb.getItemCount()>0)//图书类别不为空 { this.bookTypeJcb.setSelectedIndex(0);//表单第一项选中 } }
新闻热点
疑难解答