吉林大学数据库应用程序开发知识点总结
这是我在复习数据库应用程序开发的时候记录的笔记。
补充:我们这一年的考试题(2021级在2023年12月29考试)。
试卷共计两道题。第一题是根据实验报告出的,每道题基本上涉及一个知识点;第二题是对于吉祥码的分析题,也是以数据库为核心,操作为辅助的题目。整体难度不大,知识点都在下文提及。
知识点1 学习如何连接数据库
1.1 加载数据库驱动程序
Class.forName("COM.ibm.db2.jdbc.app.DB2DRIVER");
1.2 创建数据库的连接
String url = "jdbc:db2:samle";
String userid = "db2admin";
String passwd = "db2admin";
// 对数据库进行连接
Connection sample =
DriverManager.getConnection(url,userid,passwd);
1.3 结果集ResultSet和Statement语句
对于完整的SQL语句(需要符合SQL语法)可以直接执行。
String sql = "SELECT * FROM JLU.EMPLOYEE WHERE EMPNO = 000010";
Statement stmt = sample.createStatement();
ResultSet res = stmt.executeQuery(sql);
1.4 标签标记PreparedStatement语句
这个语句有缺省处理的效果。可以利用?在sql语句中制造缺省值,但是在执行前利用preparedstatement进行补充。
String sql = "SELECT * FROM JLU.EMPLOYEE WHERE EMPNO = ?";
PreparedStatement pstmt = sample.prepareStament(sql);
pstmt.setString(1,deptno);//带标签标记的deptno要在前面获取到
// 如果需要补充的是int类型
// pstmt.setInt(1,deptno);
// 有几个?就需要补充几个标记
// 标记的顺序和?相同
int row = pstmt.excuteUpdate();
1.5 将结果存储到结果集
while(rs.next())
{
String data1 = rs.getStirng(1);
String data2 = rs.getStirng(2);
String data3 = rs.getStirng(3);
/*
从1开始获取数据
函数里面的逻辑可以自己rewrite
*/
}
-
rs.getString(n)读取String类型
-
rs.getInt(n)读取Int类型
1.6 关闭jdbc对象
- 关闭ResultSet
- 关闭PreparedStatement
- 关闭Connection
if(rs != null)
{
try{
rs.close();
}catch(SQLException ee){
ee.printStackTrace();
}
}
if (pstmt != null)
{
try{
pstmt.close();
}catch(SQLException ee){
ee.printStackTrace();
}
}
if (sample != null)
{
try{
sample.close();
}catch(SQLException ee){
ee.printStackTrace();
}
}
注意:关闭jdbc链接并不代表已经提交了事务。
知识点2 GUI窗口交互
2.1 使用JOptionPane进行提示输入交互
- 使用showMessageDialog进行消息提示
JOptionPane.showMessageDialog(null,"已经更新的数据"+emptno);
- 使用showInputDailog进行输入对话,会将消息存储在input中
String input = JOptionPane.showInputDialog("请输入一个emptno\n");
2.2 使用JTable创建数据库表格
这个JTable我们给出的方法比较老了。
我看ChatGPT给出的方案是:先建立一个DefaultTableModel dataModel;将数据存储到这个model中,然后再将model添加到JTable中。每次刷新dataModle,不然每次添加数据再重新创建一个JTable效率太低,而且太占内存了。
String[][] lineNames = {"编号","名","中间名","姓"};
Object[][] line = new Object[4][4];
// 使用Obeject对象只是引用,并不占据内存
dataModel = new DefaultTableModel(data,lineNames);
Jtable jt = new JTable();
jt.setModel(dataModel);
刷新的话,可以先更新line 数组里面的数据,然后
importData();
dataModel.setDataVector(line,lineNames);
// 更新DataModel就好了
Excle应该就是用这种方法开发的。
2.3 获取JTable中的数据
String cellValue = (String) tableModel.getValueAt(row, column);
知识点3:插入,删除,修改操作
- 单行插入
String sql_insert = "insert into templ (empno,firstname,midname,lastname)";
- 多行插入
String sql_insert = "insert into templ(empno,firstname,midname,lastname)";
int count = num;// 用户想插入的行数
for (int i=0; i<count; i++)
{
sql = sql +",(?,?,?,?)";
}
- 通过子查询插入
String find_insert = "insert into templ(empno,firstname,midname,lasername)";
String select_query = "select * from JLU.emplyee where ? = ?";
String find_insert = find_insert + select_query;
- 实现对结果集的任意行、任意列的修改
需要知道这个实现的思路就是先导入到物理存储上,然后对物理存储修改,再写入数据库。
这过程中有对锁思想的实现。(U锁的获取,老师的视频课里面讲解了)
实现方法 1:
String str = "";
String selectForUpdate = "select * from employee for update";
// 注意 for update语句。这条语句作为sql是不符合规则的,但是jdbc会知道,你会update
int finalRow = 0;
int finalColumn = 0;
int rNum = table.getSelectRow(); // 得到表格中被选中的行数
int cNum = table.getSelectColumn(); // 得到表格中被选中的列数
stmt = smple.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE); //创建可滚动,可更新的结果集
ResultSet select_rs = stmt.excutQuery(selectforupdate);
for (int i=0; i<finalRow; i++)
selct_rs.next();
if(select_rs.next())
select_rs.updateObject(finaleColumn,str);
select_rs.updateRow(); // 将结果落实到数据库(事务提交)
实现方法 2:
String str = "";
String selectForUpdate = "select empno,firstname,lastname,edlevel,job from jlu.employee for update";
int finalRow = 0;
int finalColumn = 0;
int rNum = table.getSelectRow(); // 得到表格中被选中的行数
int cNum = table.getSelectColumn(); // 得到表格中被选中的列数
stmt = smple.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
ResultSet select_rs=stmt.executeQuery(selectForUpdate);
select_rs.absolute(finalrow+1); //直接定位到第finalrow+1行
//select_rs.absolute(lastFianlrow); // 我记得有这个函数,就是定位到最后一行+1
select_rs.updateObject(finalcolmn+1,finalstr);
select_rs.updateRow();
知识点4:wasNull()方法
结果集返回的结果,如果使用String name = rs.getString(1);
再去判断name != null 的时候,判断的实际上是String类型的null,再String中“\0”就是空。
这样判断已经不是原来的rs是否为空了。
所以采用原有的rs.wasNull()可以判断是否读到的数据是空。
总结:就是wasNull()可以检查数据到底有没有读到。因为有时候那里的数据本来就是空,读到之后无法进行判断。例如我们单行插入的时候,很多数据并没有写进数据库。
try{
PreparedStatement stmt = "";
String sql = "UPDATE TEMPL SET PHONENO = ? WHERE EMPNO = '000110'";
stmt = con.prepareStatement(sql);
if (some condition)
{
stmt.setString(1,null);
// stmt.setNUll(1,java.sql.Type.STRING);
}else{
stmt.setString(1,newPhone);
}
int updateCount = stmt.excuteUpdate();
if (updateCOunt != 1)
{
JOptionPane.showMessageDialog(null,"报错");
}
}catch(SQLException ex){
ex.printStackTrace();
}
知识点5:结果集的进一步学习
5.1 使用last, previous,first遍历
String sql = "select * from staff";
PreparedStatement stmt = sample.prepareStatement(sql);
ResultSet rs = stmt.executeQuery();
rs.last(); // 最后一条
rs.previous(); // 向前遍历
rs.first(); // 第一个
rs.next(); // 下一个
rs.absolute(position); // 绝对位置position
5.2 批处理
Statement对于批处理留下了两个接口
-
void addBatch(String sql)
相当于把sql添加到batch中一个一个执行。
-
int[] executeBatch()
执行batch中的sql命令
Statement stmt = sample.createStatement();
stmt.addBatch(sql1);
stmt.addBatch(sql2);
stmt.addBatch(sql3);
int[] updateCounts = stmt.executeBatch(); // 返回的是影响数据库中行数的更新计数
sample.commit(); //提交事务
5.3 获取数据库结构
DatabaseMetaData dbmd = sample.getMetaData(); // 获取数据库结构
String[] tableTypes = {"TABLE","VIEW"};
ResultSet rs = dbmd.getTables(null,"UDBA","%",tableTypes);
/*
dbmd.getTables(String catalog,String schema,String tableName,String[] types)
String[] types想要返回何种表的数组
只会返回10字段的描述信息。
*/
知识点6:大对象(BLOB类型)的读取
6.1 blob对象作用
blob存放二进制字符串,适用于存放UDT(User-defined Distinct Types),因此我们可以存放图片,声音等等数据。blob对象取决于varchar的大小,varchar最大是2GB,因此blob, clob, dblob 的大小都限制在2GB。
一般来说,为了提高性能,数据库需要专门创建一个用于存放大字段的表空间,数据表的大字段列应该将数据存放于对应的表空间中,这是因为不经过内存(缓冲池)直接读取的。
6.2 BLOB类型的赋值
setBinaryStream(int n, java.io.InputStream x, int length)
// n指示参数编号的int
// InputStream对象
// length指示字节数的int
6.3 查询图片
代码逻辑:
先将Blob对象读取到rs结果集中,在用getBinaryStream()将Blob对象转换成二进制流对象inputStream;
之后将inputStream流对象通过.read()方法读到int c中,将c通过OutPutStream写到OutPutFile中。
Statement stmt = sample.createStatement();
ResultSet rs = st.excuteQuery("SELECT IMAGE FROM EMPLYEE");
while(rs.next())
{
Blob blob = (Blob) rs.getBlob(1); // 读取Blob
// Blob转换为数据流
java.io.InputStream inputStream = blob.getBInaryStream();
// 定义要写入的文件
File fileOutPut = new File(path);
FileOutPutStream fo = new FileOutPutStream(fileOutPut);
int c;
while((c = inputStream.read())!= -1)
fo.write(c);
fo.close();
}
6.4 插入图片
PreparedStatement pstm = conn.prepareStatement("insert into emp_photo where emptno = ?");
pstm.setString(emptnum);
File file = new File(path);
BufferedInputStream imageInput = new BufferedInputStream(new FileInputStream(file));
pstm.setBianryStream(1,imageInput,(int)file.length);
pstm.excuteUpdate();
知识点7:大对象(CLOB类型)的读取
7.1 clob的作用
存储字符串类型的数据。举例:resume中都是一部分一部分的。因此可以作为大对象存储。
Clob resumeLob = null; // 定义Clob
resumeLob = rs3.getClob(3); // 将结果集中第三个位置的Clob取出
String detailInfo = resumeLob.getSubString((long)),(int)resumeLob.length()); // 将Clob类型黄钻换成字符串类型
POSSTR(RESUME,'Personal'); // 查看Personal在Resume中的位置
SUBSTR(RESUME,1,length); // 查看resume中1-length长度的字串
// 上面的两种语句详细用法
sql3 =
"SELECT EMPNO, RESUME_FORMAT," +
"SUBSTR(RESUME,1,?)|| SUBSTR(RESUME,?) AS RESUME " +
"FROM JLU.EMP_RESUME " +
"WHERE EMPNO = ? AND RESUME_FORMAT = 'ascii'";
/*
因为我们只知道一部分的位置,可以用substr推断另一部分的位置
*/
这里的POSSTR和SUBSTR是在sql语句中增加的,详细的用法可以看老师给出的CLOB的示范代码。
知识点8:SQLException和SQLCODE
老师讲:数据库中很多代码都是进行错误处理的。因为没有一个代码是没有错误的,因此我们尽可能设计异常处理,来捕获错误,提高系统的鲁棒性。
8.1 SQLCODE和SQLSTATE
SQLCODE:是由数据库公司进行设计的,涵盖所有的SQLSTATE之外还可以附加其他的state;
SQLSTATE:是由国际标准组织指定的。
8.2 SQLException
是由jdbc创造的数据库异常捕获模块。
常见的用法及代码如下:
catch(SQLExecption sex){
sex.printStackTrace();
int SQLCODE = sex.getErrorCode();
if(sex.getSQLState().equlas("23505"))
JOPtionPane.showMeaasgeDialog(null,"主键重复");
}
常见的SQLCODE(DB2)
switch(SQLCODE){
case 802:
//数据溢出
JOptionPane.showMessageDialog( null , "数据溢出" ,"SQL错误" , JOptionPane.ERROR_MESSAGE) ;
break;
case -007:
//SQL语句中有非法字符
JOptionPane.showMessageDialog( null , "SQL语句中有非法字符" ,"SQL信息" , JOptionPane.ERROR_MESSAGE) ;
break;
case -010:
//丢失引号
JOptionPane.showMessageDialog( null , "SQL语句丢失引号" ,"SQL信息" , JOptionPane.ERROR_MESSAGE) ;
break;
case -060:
//某特定数据类型的长度或者标量规范无效
JOptionPane.showMessageDialog( null , "某特定数据类型的长度或者标量规范无效" ,"SQL信息" , JOptionPane.ERROR_MESSAGE) ;
break;
case -102:
//字符串常量太长
JOptionPane.showMessageDialog( null , "字符串常量太长" ,"SQL信息" , JOptionPane.ERROR_MESSAGE) ;
break;
case -104:
//SQL语句中遇到非法符号
JOptionPane.showMessageDialog( null , "SQL语句中遇到非法符号" ,"SQL信息" , JOptionPane.ERROR_MESSAGE) ;
break;
case -433:
//指定的值太长
JOptionPane.showMessageDialog( null , "指定的值太长" ,"SQL信息" , JOptionPane.ERROR_MESSAGE) ;
break;
case -405:
//数值文字超出了范围
JOptionPane.showMessageDialog( null , "数值文字超出了范围" ,"SQL信息" , JOptionPane.ERROR_MESSAGE) ;
break;
}