总结:JAVA小项目——图书管理系统
一、用到的工具
1.eclipse+windowbuilder插件
2.MySQL数据库+SQLyog
二、涉及的知识点
1. MVC设计模式
2. Swing
3. JDBC+MySQL
三、框架搭建
按照MVC设计模式在eclipse中新建项目,导入项目中要用到的图标,在数据库中创建表(以及表之间的主外键关联),用JDBC知识成功连接数据库。
【JAVA MVC】
即把一个应用的输入、处理、输出流程按照Model、View、Controller的方式进行分离,这样一个应用被分成三个层——模型层、视图层、控制层。
M——Model(模型)。业务流程/状态的处理以及业务规则的制定。把抽象的概念化成一个个类,例如User、Book、BookType。
V——View(视图)。视图接收来自Model的数据并显示给用户,以及将用户界面的输入数据和请求传递给Controller和Model。此部分用windowbuilder插件来实现。
C——Controller(控制器)。这部分主要是用来连接Model和View这两部分,控制层收到请求后, 并不处理业务信息,它只把用户的信息传递给相应的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]存储大小为输入数据字节的实际长度,而不是 n 个字节。
2.【主外键关联】
主键的主要作用是将记录和存放在其他表中的数据进行关联,在这一点上,主键是不同表中各记录间的简单指针,不能有重复的,不允许为空。
外键是另一表的主键,可以有重复,可以是空值,用来和其他表建立联系用的。所以说,如果谈到了外键,一定至少涉及两张表。外键约束主要用来维护两个表之间数据的一致性。
在SQLyog软件中,点击“架构设计器”拖动要关联的两张表,如下图所示。
【JDBC】
1.【数据库中创建表】t_User,设置用户名及密码。
2.【驱动】下载MySQL相应的驱动包,新建jdbc文件夹,将包复制粘贴到此文件夹中,然后Build Path—>Addto Build Path,就将此包添加到项目中了。
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
/**
* 图书修改事件处理
* @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);//表单第一项选中
}
}