赞
踩
Test.java
,做一个转帐业务,那实际上,转账我们说还要做一些必要的验证工作呢,比如你得看看,那个转出帐号的中的钱够不够啊,是吧,等等那些事啊。addBatch(String sql)
,原来咱们想执行的话,你调execute,什么Update啊,executeQuery,那你是不是就是直接就执行了那条sql语句,是吧。addBatch(String sql)
,这是Statement原生的就支持的方法,就是增加一个批操作啊,把你的sql语句先搁到,addBatch(String sql)
里,就是先不发给数据库啊,那你这想执行一个,就先放到addBatch(String sql)这,想执行一个,就放到这啊,当你都放好了以后,你这块干嘛呢,executeBatch()
,就是你一次性执行这一批,那就相当于把那个,刚才那个存的那些sql语句,一口气全发给数据库,让它去执行,这样的话,可以减少网络调用啊,因为服务器和数据库可能不是在一个地方,它们之间的数据传输是需要网络开销的。然后呢,当你这些事都干完了以后,最后可以怎么样呢,clearBatch()
一下,把你本地这些缓存清了啊,就完事了。try-catch-finally
,建立连接等基础工作做完以后,我们在try里面,比如说啊,咱们在这往表里面插1000条记录,那我应该怎么干,实际上,我们使用for循环,如果我们使用普通的Statement的话,这么写啊,Statement state = conn.createStatement();
,但实际上,如果你要里面有动态数据的话,其实就不应该用它,这样的话,你执行计划会生成很多次,是吧,这个不理想啊,但是咱们先用着,用Statement的目的是告诉大家,其实它也有批操作啊,`for(int i=0; i<100; i++) {}``,少来点吧,就100条记录,这个当然就无所谓了啊,那这样的话,就相当于我执行很多次,是吧。String sql = "INSERT INTO userinfo_Van "
+ "(id,username, password, nickname,account) "
+ "VALUES "
+ "(seq_userinfo_Van_id.NEXTVAL,'test','123123','test',5000)";
INSERT INTO userinfo
,是吧,然后比如说加"(id,username, password, nickname,account) "
,然后呢,"VALUES "
,给值,"(seq_userinfo_Van_id.NEXTVAL,'test','123123','test',5000)";
,这个地方如果要是有这个动态的信息,是不是就不太适合使用Statement来执行了,是这样的吧,那像我这,如果没有任何动态信息的话就无所谓了,是这样的吧,那当然,这个咱们给它加一个东西,加一个吧,用户名改成"test"+i
,这样的话,就其实不太适合使用它了啊,但是咱们用它说问题,"(seq_userinfo_Van_id.NEXTVAL,'test"+i+"','123123','test',5000)";
,那比如说,我这个sql语句写好了,那么下一步,咱们原来是不是就执行了,那怎么执行啊,直接调Statement点executeUpdate(sql)
,是这样的吧,执行一条sql语句,state.executeUpdate(sql);
,那这样的话呢,我们就执行了。state.executeUpdate(sql)
,这条语句先不执行,我干嘛呢,我调用这个,Statement的一个方法叫作,addBatch(sql);
,也就是说,先把这条sql语句缓存,我先这个缓存到这个批操作中啊,那么这样的话,缓存好了以后,大家注意,state.addBatch(sql);
,你这句话,发给数据库了么,并没有,是在我们自己内存当中的,清楚吧,那比如说,当我这1000条,都干完了以后,我干嘛呢,一口气发送,它怎么一口气发送呢,你们看啊,我这要是一出了这个循环,是不是相当于这1000条信息是不是都缓存进去了,对吧,for(int i=0; i<100; i++) {
String sql = "INSERT INTO userinfo_Van "
+ "(id,username, password, nickname,account) "
+ "VALUES "
+ "(seq_userinfo_Van_id.NEXTVAL,'test"+i+"','123123','test',5000)";
//每次调用executeUpdate()都会发送给数据库
// state.executeUpdate(sql);
state.addBatch(sql);//先缓存到批中
}
state.executeBatch();
,那它就一次性,就把你刚才缓存的东西,全发给数据库,一口气就执行啊,而且大家注意,它的返回值是这样的,它返回给我们一个什么呢,int数组,int[]
,那返回的这个int数组,是什么意思呢,就是说,影响表中多少条数据,是吧,当然这回是个数组,原来咱们执行state.executeUpdate(sql);
方法,不是返回一个int值么,是不是影响表中多少条数据,因为你这是一次性给了1000条啊,像我这给了100条,是吧,你给了100条,是不是每一条sql语句,是不是都可以影响表中数据的,对么,那这个时候它返回的就是一个数组,而数组里面每一个元素,就是你曾经执行的其中的一个(语句),影响的表中的数据。state.executeBatch()
里面的这个返回值啊,有的时候,说我执行成功了,它返回值为什么不是1啊,它在那给我写个什么-1,什么-2,或者或几啊几,这个都是有可能的,这个东西,说返回-1或-2,并不是说,指的是执行失败了啊,这在jdbc里面呢,是有说明这个问题的啊,它是怎么说的呢,它是这样的啊,打开文档看看,比如说,找Statement的executeBatch,就这个方法,对吧,int[] executeBatch() throws SQLException
Submits a batch of commands to the database for execution and if all commands execute successfully, returns an array of update counts. The int elements of the array that is returned are ordered to correspond to the commands in the batch, which are ordered according to the order in which they were added to the batch. The elements in the array returned by the method executeBatch may be one of the following:
A number greater than or equal to zero -- indicates that the command was processed successfully and is an update count giving the number of rows in the database that were affected by the command's execution
A value of SUCCESS_NO_INFO -- indicates that the command was processed successfully but that the number of rows affected is unknown
If one of the commands in a batch update fails to execute properly, this method throws a BatchUpdateException, and a JDBC driver may or may not continue to process the remaining commands in the batch. However, the driver's behavior must be consistent with a particular DBMS, either always continuing to process commands or never continuing to process commands. If the driver continues processing after a failure, the array returned by the method BatchUpdateException.getUpdateCounts will contain as many elements as there are commands in the batch, and at least one of the elements will be the following:
A value of EXECUTE_FAILED -- indicates that the command failed to execute successfully and occurs only if a driver continues to process commands after a command fails
The possible implementations and return values have been modified in the Java 2 SDK, Standard Edition, version 1.3 to accommodate the option of continuing to process commands in a batch update after a BatchUpdateException object has been thrown.
SUCCESS_NO_INFO
,什么意思啊,就是这条语句执行成功了,但是没有任何结果啊,就是数据库没有给我们任何回应,原因是什么呢,它说受影响的行数是未知的啊,那这句话怎么理解,其实它太草率了,这句话说的,告诉大家一点,是因为不同的jdbc厂商,就是我们那个数据库厂商,它在实现jdbc接口的时候,它返回,它就按照它自己的意愿返回了,那可不一定大家都一样,只是说我们说接口的方法都一样,是吧,但是返回,它这个概念,有可能不是按照你说。SUCCESS_NO_INFO
,注意这东西,我们有什么呢,有常量去对应的啊,都在这啊,/** * The constant indicating that the current <code>ResultSet</code> object * should be closed when calling <code>getMoreResults</code>. * * @since 1.4 */ int CLOSE_CURRENT_RESULT = 1; /** * The constant indicating that the current <code>ResultSet</code> object * should not be closed when calling <code>getMoreResults</code>. * * @since 1.4 */ int KEEP_CURRENT_RESULT = 2; /** * The constant indicating that all <code>ResultSet</code> objects that * have previously been kept open should be closed when calling * <code>getMoreResults</code>. * * @since 1.4 */ int CLOSE_ALL_RESULTS = 3; /** * The constant indicating that a batch statement executed successfully * but that no count of the number of rows it affected is available. * * @since 1.4 */ int SUCCESS_NO_INFO = -2; /** * The constant indicating that an error occurred while executing a * batch statement. * * @since 1.4 */ int EXECUTE_FAILED = -3; /** * The constant indicating that generated keys should be made * available for retrieval. * * @since 1.4 */ int RETURN_GENERATED_KEYS = 1; /** * The constant indicating that generated keys should not be made * available for retrieval. * * @since 1.4 */ int NO_GENERATED_KEYS = 2;
SUCCESS_NO_INFO
,然后它的常量值,你看它得-2,明白吧,有可能一会你发现,它那数组里面,一溜的-2,那-2,不是说表示我一条没插进去,还欠它两条,不是这意思啊,就是说,它还是成功了的,明白吧,就是这东西,你要不看文档,你就就就呈费解了,是吧,所以我们说,还是要自己去看一看文档啊,那总之,我们说你这样的话,一执行的话,就一口气,全发过去了,明白吧,那么,执行完了以后呢,我们这块正常情况下,你就去输出执行完毕,但大家注意,这样的话,效率很低的,为什么说效率低,咱们这块仅仅是把什么啊,网络传输这个地方,是不是给提高速度了,对吧,但是有两个没有解决,一个就是执行计划生成没管,还有一个那个,就是事务没管,是吧,你要想飞快,那你得3条都考虑,对吧。String sql = "INSERT INTO userinfo_Van "
+ "(id, username, password, nickname, account) "
+ "VALUES "
+ "(seq_userinfo_Van_id.NEXTVAL,?,'123123','test',5000)";
INSERT INTO userinfo
,然后呢,(id,username,password,nickname,account)
,然后呢,VALUES,然后呢,(seq_userinfo_id.NEXTVAL,?,?,?,5000)
,然后,问号,问号,问号,这就别问号了,这个这个,密码也别问号了,统一的,123123,我就写一个问号了,反正明白意思就行了啊,行吧,我就写一个问号了,这明白怎么回事就行了,就相当于这个问号,是一个变化的值,(seq_userinfo_id.NEXTVAL,?,'123123','test',5000)
,是吧,然后,那我想执行1000次的话,首先,第一点,咱们要先创建PreparedStatement,是吧,PreparedStatement ps = conn.prepareStatement(sql);
,然后就把sql语句给进去,是吧。for(){}
循环,然后呢,咱们在里面就替换问号了,是吧,咱们原来怎么做的,替换问号,是不是就是ps.set
,比如说String,是吧,第一个问号的值,它比如说是那个,"test"+i
,然后呢,我们是不是要正常执行,就调executeUpdate,是这样的吧,当你调完这executeUpdate,就等同于什么啊,它不就把这问号里的值发给数据库了,你执行一下,刚才生成好了那个执行计划,是这意思吧,你使用PreparedStatement的好处在于什么呀,执行计划是不是已经提前生成好了啊,你只需要每次传问号对应的值就完了,那你这样的话,你每一次执行,是不是都会把这问号的值,传一遍过去的,是这样的吧。ps.addBatch()
,而且这块的addBatch,是不是就不用传sql语句了,为什么上面的那个要传啊,就是因为Statement,你每次执行的时候,是不是都要把这sql语句发过去的,对么,而PreparedStatement,已经上来,已经把sql语句发过去了,我只是不知道问号对应的值了么,所以实际上你只是,把每次你设置的那个问号,对应的值干嘛呢,缓存起来了,也就是说,你这ps.addBatch()
,缓存了一次,第一次缓存,那个值就知道啊,这个问号代表的值就是这个"test"+i
,你下次再来的时候,是不是就是另一组了,明白吧,等于说你把每一组问号换的值,都在这里存好,然后,最后干嘛呢,也是ps.executeBatch();
,能理解吧,那同样你可以得到这个int型的数组啊,然后呢,是不是就执行完毕了,对吧。conn.setAutoCommit(false);
,然后在这个地方,都完事了以后,在这块,conn.commit();
,大家注意,那这样的话,执行速度是最快的,一个事务里,是吧,一个执行计划,一次网络传输,全搞定了啊,那也就是说,将来等你们真的要,批量操作的时候,就应该这么干。int[] data = state.executeBatch();
,这个是使用传统的Statement执行executeBatch,PS执行的批处理是int [] data = ps.executeBatch();
,一样的方法,另外,这个地方还会有一个问题啊,将来比如说,我们批操作的话,说这个也不能无止境的往ps.addBatch();
里面发啊,比如说,我准备发送一万条数据,是吧,全搁到批里,一口气过去一万个,你也得看你的内存承载能力啊,有的时候,你这搁着,搁着,内存溢出了啊,这种情况也存在,所以你可以干嘛呢,比如说在for循环里面是不是也可以记一下,比如说,我每500条提一次,每500条提一次,可不可以,我们想每500条提一次怎么做啊,我先是不是可以。int count = 0;
,然后呢,你在里面加了一次呢,就什么count++;
一次,是这意思吧,然后,如果比如说,if(count%500==0) {}
,什么意思,就是它是500的倍数了,500的倍数,就说明它够500条了,然后就可以在这个if里面干嘛呢,执行一次,ps.executeBatch();
,就是一次过去500条,清楚吧,过去完了以后呢,ps.clearBatch();
,把你之前这个缓存那些东西,先从你本地清了,明白吧,清掉以后,你再执行,再执行,那你就,这样就完了,能理解么,你这么干就等于说,500条过去一次,500条过去一次,你也别就一口,当然我们这本身才,总共才循环100次,但我是那个意思,明白么,像在我这块呢,比如说我每50次过去一次,清楚吧,你这样的话,你就能保证,你这个也是分段。executeBatch()
一次啊,int [] data = ps.executeBatch();
,也可以比如说,是不是executeBatch()
可以得到这个数组了么,是吧,你可以放入循环,把这个数组输出一下,看看哪一个值都是什么,可以先输出一下数组的length啊,然后再输出一下这个数组里的每个元素,看看执行完了以后是什么,你有可能看到的,就是一堆的-2,是吧,-2什么意思来着,success_no_info
,是吧,就是它执行成功了,但是没有消息啊,这种情况也存在啊,这个是由于这个不同的这个数据厂商的,提供的jdbc驱动的时候,它的这个实现可能不一样啊,所以它里面,有可能返回的不是具体影响了多少条记录了啊。package demo; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.Statement; /** * 批操作 * 批操作是可以将要执行的大量SQL语句缓存在本地 * 然后一次性发送给数据库,减少网络调用,调高 * 执行效率。 * @author soft01 * */ public class JDBCDEMO_batch { public static void main(String[] args) { Connection conn = null; try { conn = DBUtil.getConnection(); //Statement state = conn.createStatement(); /*for(int i=0; i<100; i++) { String sql = "INSERT INTO userinfo_Van " + "(id,username, password, nickname,account) " + "VALUES " + "(seq_userinfo_Van_id.NEXTVAL,'test','123123','test',5000)"; // + "(seq_userinfo_Van_id.NEXTVAL,'test"+i+"','123123','test',5000)"; //每次调用executeUpdate()都会发送给数据库 // state.executeUpdate(sql); state.addBatch(sql);//先缓存到批中 } int[] data = state.executeBatch();*/ conn.setAutoCommit(false);//把事务打开 String sql = "INSERT INTO userinfo " + "(id, username, password, nickname, account) " + "VALUES " + "(seq_userinfo_id.NEXTVAL,?,'123123','test',5000)"; PreparedStatement ps = conn.prepareStatement(sql); int count = 0; for(int i=0; i<100; i++) { ps.setString(1, "test"+i); // ps.executeUpdate();//原来的方案 ps.addBatch(); count++; // if(count%500==0) { if(count%50==0) { ps.executeBatch(); ps.clearBatch(); } } int [] data = ps.executeBatch(); int d=data.length; System.out.println("data.length:"+d); for(int i=0;i<d;i++) { System.out.print(" "+data[i]); } conn.commit(); System.out.println("执行完毕!"); } catch (Exception e) { e.printStackTrace(); }finally { DBUtil.closeConnection(conn); } } }
***
这么回事,什么这那,这那,告诉他,实际上我们说是,一组服务器来架这一个游戏的啊,所以为什么,有时候你会发现,你能打怪啊,能聊天,就是拍卖行点不了,为什么呢,拍卖行服务器挂了,就这台服务器宕机了,明白吧,那就这个功能,你使不了,明白么,那就等着什么啊,跟GM说一下,让他重启一下,那个机房把这台机器给我重启一下就好了,明白吧,他们都是这么干啊,哎,我怎么说到这了啊,log,是吧,说到那个log服务器啊。CREATE TABLE dept_Van( deptno NUMBER(2,0), dname VARCHAR2(14), loc VARCHAR2(13) ) DESC dept_Van INSERT INTO dept_Van (deptno,dname,loc) VALUES (10,'ACCOUNTING','NEW YORK'); INSERT INTO dept_Van (deptno,dname,loc) VALUES (20,'RESEARCH','DALLAS'); INSERT INTO dept_Van (deptno,dname,loc) VALUES (30,'SALES','CHICAGO'); INSERT INTO dept_Van (deptno,dname,loc) VALUES (40,'OPERATIONS','BOSTON'); SELECT * FROM dept_Van COMMIT
1.通过序列产生主键(Oracle): 在Oracle中,建议主键通过序列获得: String sql = "insert into dept (deptno, dname, loc) values(dept_seq.nextval,?,?)"; 如果单表操作,不需要返回刚刚插入的主键值;如果关联操作,需要将刚刚插入的主键值返回。 2.方法一:先通过序列的nextval获取序列的下一个值,再作为参数插入到主表和从表: 获得主键:String sql = "select dept_seq.nextval as id from dual"; 插入主表:String sql1 = "insert into dept(deptno, dname, loc) values(?,?,?)";...ps.setString(1, id);... 插入从表:String sql2 = "insert into emp(empno, ename, deptno) values(?,?,?)";...ps.setString(3, id);... 简单,但需要额外多一次访问数据库,影响性能。 3.方法二:利用PreparedStatement的getGeneratedKeys方法获取自增类型的数据,性能良好,只要一次SQL交互。 sql = "insert into dept (deptno, dname, loc) values(dept_seq.nextval,?,?)"; //stmt的第二个参数是GeneratedKeys的字段名列表,字符串数组 stmt = con.preparedStatement(sql,new String[]{"deptno"}); stmt.setString(1,"IT"); stmt.setString(2,"Beijing"); stmt.executeUpdate(); //获取主键值所在的rs rs = stmt.GeneratedKeys(); rs.next(); //从rs中取出主键值,从表示用 int deptno = rs.getInt(a);
String sql = "select dept_seq.nextval as id from dual";
,这是不是从一个序列里,调nextval,现在等于说,from 伪表, 等于说,你是不是单独先从这个序列当中,获取了一个值,然后得到这个值以后呢,你是不是再把这个值作为员工表的主键插进去啊,String sql1 = "insert into dept(deptno,dname,loc) values(?,?,?)";
,作为,再作为员工表的外键,是不是才把那条记录插进去,String sql2 = "insert into emp(empno,ename,deptno) values(?,?,?)";
,也就是说,那你至少得有一个单独获取,部门表这个主键字段值的这么一个操作,是吧,那这个不理想。sql ="insert into dept(deptno,dname,loc) values(dept_seq.nextval,?,?)";
,你看这都写好,然后这个values,值是不是就是用那个序列的下一个数,是吧,然后后面呢,是两个问号,就给值,然后呢,我创建preparedStatement,stmt = con.preparedStatement(sql,new String[]{"deptno"});
,因为上面不是还有动态信息么,所以这个地方我们是不是应该使用preparedStatement,对吧,那么你这个地方呢,我们就把这个sql语句给进去,同时大家,原来我们是不是没有传过第2个参数,原来咱们创建PreparedStatement,我们是不是把这sql语句传进去就行了,是吧,那实际上PreparedStatement还支持第2个参数啊,那它第2个参数是一个字符串数组,这数组里面,每个元素是什么呢,就是你希望在执行完这一条,要插入的insert,预编译语句以后,得到那条记录里面哪个字段的值,你就在这个第2个参数里指明就行了。stmt.setString(1,"Research");stmt.setString(2,"beijing");stmt.executeUpdate();
,然后executeUpdate,这时候,是不是这条记录就插进去了,是吧,插到表里去了,插到表里去以后呢,这个时候就调用,rs=stmt.getGeneratedKeys();
,它有一个方法叫getGeneratedKeys()
,就是获取生成的键,就是它那个GeneratedKeys,就是生成的键,是吧。getGeneratedKeys();
,这个方法的时候,你可以得到一个结果集,而这个结果集就是对应的是你刚才,你要指定的那个字段对应的值是什么,它就可以把它返回回来啊,而这个里面只会有一条记录,因为你只插了一条记录进去,而那个记录里面,这个结果里面只有一个字段,就是你上面指定的字段,就是你要这个字段的值啊,它就返回给你了啊,rs.next(); int deptno = rs.getInt(1);
,如果我们要写的话,就类似于这个样子,我给大家分开,在sqldeveloper,这块给大家演示,大概是什么意思啊,就好比说啊,我应该没有这个序列吧,创建一个啊,CREATE SEQUENCE seq_dept_id
START WITH 1
INCREMENT BY 1
INSERT INTO dept
(deptno,dname,loc)
VALUES
(seq_dept_id.NEXTVAL,'IT','BeiJing');
stmt.executeUpdate();
,生成preparedStatement的时候,什么呢,我告诉你了,我要你插入stmt.setString(1,"Research");stmt.setString(2,"beijing");
,这条记录以后,是不是要deptno这个字段的值,那就意味着相当于什么呢,我在执行这条语句以后,我要你插入这条记录里面dept这个字段对应的值,那就相当于,这条记录插进去以后,它就自动干了一件事情,什么事呢,就是把这个deptno字段的值获取回来了啊,那就相当于是那个值啊,那这个时候大家注意,那当我们插入完毕以后,这块stmt.executeUpdate();
,执行完毕以后,你一调rs=stmt.getGeneratedKeys();
,那大家注意,它会返回什么啊,返回给我们一个结果集,而这个结果集是谁呢,就是你刚刚插进去,这条记录当中,对应哪个字段啊,deptno这个字段的值,就相当于这个当中的一个结果集回给你了,明白么。rs.next();
,也就是说,我们就相当于把那个指针,指向了第1条记录,发现它是有的,是吧,然后呢,你是不是获取第一个字段,rs.getInt(1);
,因为这是一个整数吧,你调getInt的话就相当于就把这个数字1就得到了,就把这个,相当于把这个指针的值就给取到了,对吧,但如果大家注意啊,我在这写了两个字段,除了这个deptno之外,我写一个dname,比如说,我还写了一个dname,那就得到了,deptno和dname这两个字段,对应的值,当你调stmt.getGeneratedKeys()
,你就得到了这两个,明白么,得到着两个呢。try-catch-finally
块,DBUtil创建连接,关闭连接等工作都做好啊,那么现在,比如说,我们现在就往部门表里插入一条数据,然后在插入的同时呢,把它那个主键值得到,那我怎么做啊,首先咱们写一条sql语句,然后sql语句就是insert into dept
,部门表是吧,然后呢,我们说咱们给值(deptno,dname,loc),然后呢,values,这块是,(seq_dept_id.NEXTVAL,?,?);
,是这样的吧。String sql = "INSERT INTO dept_Van "
+ "(deptno,dname,loc) "
+ "VALUES "
+ "(seq_dept_Van_id.NEXTVAL,?,?) ";
PreparedStatement ps = conn.prepareStatement(sql, new String[] {"deptno"});
,当你这条sql,你执行一次以后,你不执行一次插入么,获取当你插入那条记录当中对应的部门号的值啊。ps.setString
,第2个问号,是Beijing是吧,然后呢,ps.executeUpdate();
,执行完是不是就插进去了,对吧,PreparedStatement ps = conn.prepareStatement(sql, new String[] {"deptno"});
ps.setString(1,"IT");
ps.setString(2, "Beijing");
ps.executeUpdate();
ps.getGeneratedKeys();
,然后就那个得到了,我得到一个结果集,而这个结果集应该是只有一行一列的,为什么呢,因为你执行ps.executeUpdate();
,这条语句,这条语句是不是只会往表里插一条数据,是吧,然后你还只要这一个字段的值,那也就是说,我们会获取到什么呢,你查入那条记录当中,deptno那个字段的值啊,这个时候,我们就调rs.next();
,如果你不放心,你可以if(rs.next()){}
,确定它有那条记录,是这样的吧,那你插入的话,应该是没问题啊,如果有的话呢,我们调int id = rs.getInt(1);
,为什么调getInt啊,因为部门号是一个整数,而这个地方,我们写1,就是第一个字段,这个地方注意,不要写字段名,因为它不是以字段名的形式,返回给你的啊。虽然我这个地方,说我要的这个字段啊,但是这个地方不要用字段名啊,直接用这个位置,这是获取这个第1个字段的值是吧。int id
,也就是说我们插入一条记录了,比如说,插入的部门id是,加上id,System.out.println("插入的部门ID是:"+id);
,那我们来看看啊,执行,现在大家看插入的id,部门号是几啊,是2,为什么是2啊,因为刚从我在sqldeveloper测试的时候,是不是把1用了,是这样的吧,那这个时候,我们来sqldeveloper查这个部门表的话啊,from * from dept
,大家看这个是不是2,也就是说,我刚才是不是,确定把这条记录插进去了,而且,我是不是同时就把这个id值得到了,将来你拿到这个id值以后呢,你是不是就可以用这个id值,接着往员工表里插数据了,那他们的部门号,是不是就可以对应的是2,明白这意思吧。package demo; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; /** * 返回自动主键 * @author soft01 * */ public class JDBCDEMO_generatedKeys { public static void main(String[] args) { Connection conn = null; try { conn = DBUtil.getConnection(); String sql = "INSERT INTO dept_Van " + "(deptno,dname,loc) " + "VALUES " + "(seq_dept_Van_id.NEXTVAL,?,?) "; /* * 创建PS的同时指定执行该PS对应的SQL语句后 * 要得到插入记录中指定字段的值 */ PreparedStatement ps = conn.prepareStatement(sql, new String[] {"deptno"}); ps.setString(1,"IT"); ps.setString(2, "Beijing"); ps.executeUpdate(); //获取插入的数据中指定字段的值 ResultSet rs = ps.getGeneratedKeys(); rs.next(); int id = rs.getInt(1);//获取第一个字段的值 System.out.println("插入的部门ID是:"+id); } catch (Exception e) { e.printStackTrace(); }finally { DBUtil.closeConnection(conn); } } }
Date Access Object
,叫作什么呢,数据连接对象啊,或者叫数据访问对象啊,那么大家注意,DAO,听我说啊,不是针对的是具体指的某一个类,它是指的是某一个层次,就跟我们说,咱们不是可以把一堆类,比如这些类都是负责处理业务的,那我们叫业务层,就是这个层次里面的所有类都是处理业务逻辑的。package entities; /** * UserInfo类用于表示userinfo表 * 其每一个实例用于表示userinfo表中的一条记录 * @author soft01 * */ public class UserInfo { private int id; private String username; private String password; private String nickname; private int account; public UserInfo() { } public UserInfo(int id, String username, String password, String nickname, int account) { super(); this.id = id; this.username = username; this.password = password; this.nickname = nickname; this.account = account; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public int getAccount() { return account; } public void setAccount(int account) { this.account = account; } public String toString() { return id+","+username+","+password+","+nickname+","+account; } }
public boolean save(UserInfo userInfo) {}
,那么在这个地方呢,实际上就相当于,到时候这个业务逻辑层会把这个,它想保存这个用户,那个信息呢,变成一个UserInfo的对象交给我们,那在这个地方呢,这个save()方法里,我们就可以把它插到数据库里头去了,也就是说现在和数据库打交道的是由UserInfoDAO来干了啊,那就不再交给,原来我们的Servlet去干了啊,那么在这个地方,我们怎么做呢,首先,这个save方法里面,其实就跟之前一样了,首先Connection conn=null;
,是吧,声明一个连接对象。try-catch-finally
和DBUtil关流,try里面DBUtil建立连接等基础工作,如果之前这块没有成功,出来以后,我们return false
,告诉你没有保存成功,那如果我在try这个里面呢,拿到它(连接)以后,咱们是不是就可以开始往里面插数据是吧,那这个时候首先创建sql语句,String sql = "INSERT INTO userinfo_Van "
+ "(id,username,password,nickname,account) "
+ "VALUES "
+ "(seq_userinfo_id.NEXTVAL,?,?,?,?)";
seq_userinfo_id.nextval
,是吧,然后呢,四个问号啊,然后呢,我们就开始创建PreparedStatement,是吧,它等于Connection点prepareStatement,然后把sql语句给进去,那么我们知道一点,实际上,由于上面的id是不是我们提供的,实际上就是将来它(客户端)给我的时候,就可能没有id这项啊,但是当我们插入完以后呢,这id就有了,我们把id呢,就也还给它设置回到这个业务层传过来的userinfo对象上,让userinfo对象,它去表示这条记录,一个包含完整信息的userinfo实例,以备在注册功能之后,有一些其他的相关联的操作需要用到这个userinfo对象的信息。new String[]{"id"}
,这块我们要什么呢,返回自动主键,在插入的同时得到序列自动生成的这个id的值,是吧,PreparedStatement ps = conn.prepareStatement(sql,new String[] {"id"});
。ps.setString(1, userInfo.getUsername());
,就是第一问号,不就是这个的值么,是吧,然后同样,ps.setString(2, userInfo.getPassword());
,第2个对象呢,是从userinfo里面去取的密码,是吧,然后,ps.setString(3, userInfo.getNickname());
,第3个问号呢,是userinfo.getNickname()
,然后,ps.setInt(4, userInfo.getAccount());
,是吧,我不记得,当时我们那个有小数位么,account我不记得了,但是有小数位的话,你这应该用double,咱们那account应该在设置的时候,应该也是double类型,是这样的吧,如果要整数类型呢,那这块就写setInt了啊。那么这是userinfo.getAccount()
。int d = ps.executeUpdate();
,是吧,我执行这条sql语句啊,然后int d
等于它,如果d>0
,它就说明这条记录插进去了,是吧,插进去以后,这块实际上就什么呢,注册应该是成功了,就是我这条记录应该已经是插到表里去了,是吧,那咱们这就不写,如果跟业务逻辑没关系的话,我们这只是存数据的话,我们不写注册成功,就是什么啊,插入数据成功是吧,就相当于我们这条记录是保存到表里去了,然后呢,我们是不是就可以把对应的id值取出来,怎么取啊,ResultSet,是吧,它等于ps.getGenerateKeys()
,是这样的吧,把这个字段的主键,把它返回回来,即ResultSet rs = ps.getGeneratedKeys();
。rs.next()
,走一条,然后呢, int型的id,它就等于rs.getInt(1)
,第一个字段的值,是吧,然后把它设置回到userInfo上,setId(id);
,是吧 ,因为它之前不就是没这id么,是吧,给它设置好,都完事以后,return true;
,就表示我插入记录完毕,并且呢,你id也有了,你那userinfo就表示表里那条记录了,完整的都有了,是吧,都插进去啊,那如果不成功的话,最终出来以后,是不是再去最后return false;
,是吧,那这样的话,这一个完整的方法我们就写好了,就是一个正常的保存数据了,是吧。package dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import db.DBUtil; import entities.UserInfo; /** * DAO 数据连接对象 * DAO是一个层次,该层里的所有类都是和数据库打交道得,作用是将数据操作的功能从也无逻辑层中分离出来, * 使得业务逻辑层更专注的处理业务操作,而对于数据的维护操作分离到DAO中。 * 并且DAO于业务逻辑层之间是用JAVA中的对象来传递数据,这也使得有了DAO可以让业务逻辑层对数据的操作完全面向对象化。 * @author soft01 * */ public class UserInfoDAO { /** * 保存给定的UserInfo对象所表示的用户信息 * @param userInfo * @return */ public boolean save(UserInfo userInfo) { Connection conn = null; try { conn = DBUtil.getConnection(); String sql = "INSERT INTO userinfo " + "(id,username,password,nickname,account) " + "VALUES " + "(seq_userinfo_id.NEXTVAL,?,?,?,?)"; PreparedStatement ps = conn.prepareStatement(sql,new String[] {"id"}); ps.setString(1, userInfo.getUsername()); ps.setString(2, userInfo.getPassword()); ps.setString(3, userInfo.getNickname()); ps.setInt(4, userInfo.getAccount()); int d = ps.executeUpdate(); if(d>0) { //插入数据成功 ResultSet rs = ps.getGeneratedKeys(); rs.next(); int id = rs.getInt(1); userInfo.setId(id); return true; } } catch (Exception e) { e.printStackTrace(); } finally { DBUtil.closeConnection(conn); } return false; } }
userInfo.setUsername(username);
,然后呢,userInfo.setPassword(password);
,是吧,然后再userInfo.setNickname(nickname);
,然后呢,再userinfo.setAccount(5000);
,也就是说,将来这个值是多少,是由业务逻辑决定的,就是说,数据库那边不管,你让我插什么数据,我就给你插入,明白吧,它不决定那个数据具体是啥,由你这决定好,这都是业务逻辑说的算啊,比如说,我这默认情况下,比如说,我们就认为啊,用户一注册,给你5000红利,是吧,就类似这个意思啊,在这块呢,我们这么写就行了。try-catch
啊。package service; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import common.HttpContext; import core.HttpServlet; import dao.UserInfoDAO; import db.DBUtil; import entities.UserInfo; import http.HttpRequest; import http.HttpResponse; /** * 用来完成用户注册功能 * @author fhy * */ public class RegServlet extends HttpServlet { /* * 我们用这个方法来完成业务处理 */ public void service(HttpRequest request,HttpResponse response) { //此时重启服务器,客户端访问注册页面就可以在控制台显示“开始处理注册!” System.out.println("开始处理注册!");//打桩 /* * 将用户的注册信息按行写入到 * userinfo.txt文件中。 * 每行为一条用户的信息,格式: * username,password,nickname * 例如: * fanchuanqi,123456,fanfan * liucangsong,222333,cangcang * * userinfo.txt放在webapp目录下 */ //获取用户名,密码,昵称 String username = request.getParameter("username"); String password = request.getParameter("password"); String nickname = request.getParameter("nickname"); /* Connection conn = null; try { conn = DBUtil.getConnection(); //先判断该用户是否存在 Boolean isNamed = comparedName(conn, username); if(isNamed||"nothing".equals(username)||"nothing".equals(password)||"nothing".equals(nickname)) { System.out.println("用户名已存在,或输入为空!"); forward("/reg_info.html", response); } else { Statement state = conn.createStatement(); String sql = "INSERT INTO userinfo " + "(id,username,password,nickname,account) " + "VALUES " + "(seq_userinfo_id.NEXTVAL,'"+username+"','"+password+"','"+nickname+"',5000)"; int d = state.executeUpdate(sql); if(d>0) { System.out.println("注册成功!"); */ //创建一个UserInfo实例用于表示该注册信息 UserInfo userInfo = new UserInfo(); userInfo.setUsername(username); userInfo.setPassword(password); userInfo.setNickname(nickname); userInfo.setAccount(5000); //保存用户信息 UserInfoDAO dao = new UserInfoDAO(); boolean success = dao.save(userInfo); try { if(success) { /* * 响应注册成功的页面给客户端 */ forward("/reg_ok.html", response); }else { //跳转注册失败页面 System.out.println("数据库插入失败!"); forward("/reg_info.html", response); } } catch(Exception e) { e.printStackTrace(); } /* } } catch (Exception e) { //如果有异常的话,将异常输出一下。 e.printStackTrace(); }finally { DBUtil.closeConnection(conn); //closeBrAndPw(br,pw); } */ } private void closeBrAndPw(BufferedReader br, PrintWriter pw) { if(br!=null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } if(pw!=null) { pw.close(); } } private Boolean comparedName(Connection conn, String username) throws SQLException, ClassNotFoundException { boolean flag = false;//用户名可用 Statement state = conn.createStatement(); String sql = "SELECT username from userinfo where username='"+username+"'"; ResultSet rs = state.executeQuery(sql); if(rs.next()) { flag=true;//用户名已存在 } return flag; } }
localhost:8080/index.html
,然后这是那主页是吧,然后去注册啊,比如输个用户名,密码123456,然后输入用户昵称,点击注册,哎,这个时候,大家可以看到,是不是仍然可以显示注册成功了,是这样的吧,那么,但是你们得知道,咱们这块,实际上拿到这些信息以后,我是不是都交给那dao去干的,它是不是就负责帮我把这个这几个数据,最终写成一个sql语句,但大家注意,你看,对于我们来讲,在RegServlet这个地方,如果我调用dao,我把数据给dao,实际上知道插到哪去了么,根本就不知道,因为保存的事,是不是交给dao去干了,我根本就不关注数据库那些表叫什么名,跟我没关系,我只管我的业务逻辑,对了,我就把数据交给你,你保存就好了。/reg
那个位置啊,那也就是说那个/reg
,会被我们的WebServer整到哪去,会整到这个RegServlet来,是这样的吧,那也就是最终,这个过程知道怎么来的吧,咱们之前写的,在那个ClientHandler,我们处理那uri什么的,是吧,只要最终就是,总之就是拿到了这个表单中的数据,传到了RegServlet来,那我们在这干嘛呢,处理什么呢,处理业务,就说它这块是负责什么呢,处理业务的,是吧,这个地方是什么呢,就是用户提交注册信息,是吧,用户提交的这个注册信息,然后通过form表单提交到我们的RegServlet来,这Servlet呢,就负责处理业务,那它是不是就可以把,从用户那里拿到的那些信息,我们通过Request,获取那些用户名啊,密码啊,是吧。public UserInfo findByUsername(String username) {}
,就是根据用户名找人,然后如果找到的话,会返回一个UserInfo的实例。SELECT * FROM userinfo
,当然这不应该写*
号啊,我就这意思了啊,然后呢,WHERE 传的什么呢,username=''
,即 SELECT * FROM userinfo WHERE username='';
,我说一点啊,我是这么写的,我只是告诉你,这sql语句,应该是用这个,WHERE条件是username,用这个判断,因为你是不是根据用户名找么,别我这么写,你也这么写,username='"++"'
,里面就开始拼字符串了啊,这块应该是什么呢,问号还得是问号啊,这应该username=?
,还得是问号啊,这还是得有动态信息的啊,然后这个地方不要写*
号啊,我这只是示意啊。具体sql如下:String sql = "SELECT id,username,password,nickname,account "
+ "FROM userinfo "
+ "WHERE username=?";
return null;
,如果有人的话,就是不是,应该把各个字段的值都得到,然后是不是保存到一个UserInfo实例上,把这个UserInfo实例返回就好了,能理解我的意思吧,一会咱们可以用,在我们这个,作为这个检查该用户是否存在啊,大家一定要注意一个原则,就是什么呢,就是DAO啊,就是这个到,它和业务逻辑,将来打交道,都是靠对象,互相传对象啊,就是你给我用户的信息,也是以用户对象给我,我返回给你,你管我要,我也是返回给一个什么呀,UserInfo的实例啊,表示这个员工的信息,也就是说,业务逻辑层,它其实看不到数据库的影啊,就是你不需要管,反正你数据给我,我就想干嘛啊,说到底咱们俩之间交流,就是对象啊。UserInfoDAO dao = new UserInfoDAO();
,自己是不是先创建个DAO,就new一个UserInfoDAO,然后是不是可以自己组建个对象,调一下那个方法findByUsername,先看你这自己这块成不成功,如果成功的话,你将来Servlet是不是一调,也应该也是可以的,明白么,将来做测试的时候,都是这样啊。dao.findByUsername("jack");
,你这比如自己写一个,然后看看,得到那个UserInfo实例,对不对么,看看它输出的是啥,是不是都可以自己先打个桩,先测测,明白么,都这么干啊,先保证你这个,这个方法没错,然后一会咱们再配合,用Servlet调一下就好了啊,一会再写哪个方法的时候,都是先这么测啊。Connection conn = null;
,然后try-catch-finally
块,把基础的工作先做好啊,DBUtil关闭连接,建立连接,如果最后报错的话,然后我们就怎么样,我们就返回null了啊,然后在try里面建立好连接之后呢,我们先写条sql语句,这条sql应该长什么样子啊,SELECT id,username,password,nickname,account FROM userinfo WHERE username=?
,是这样的吧,然后呢,创建PreparedStatement,是吧,那就等于什么啊,Connection的方法,就是PreparedStatement(sql);
,然后呢,我们可以设置问号是吧,ps.setString(1, username);
,ResultSet rs = ps.executeQuery();
,ps执行executeQuery,是吧,得到ResultSet,得到结果集以后呢,如果,rs.next()
找到数据了,那我们就把那些信息都给它取出来,是吧,比如int型的id,等于rs.getInt(“id”);,然后,username = rs.getString("username");
,username因为上面参数已经定义过了,是吧,所以在这,我们就直接用就行了啊,然后,String password = rs.getString("password");
,然后是,String nickname = rs.getString("nickname");
,然后呢,int account = rs.getInt("account");
,这几项都有了以后,我们是不是就可以实例化这个UserInfo表示它了,userinfo,它等于new一个UserInfo,是吧,然后把这几个参数,传进去,是不是就可以了,对吧,传进去以后呢,我们就return这个userinfo,就完事了吧,是这样的吧。public UserInfo findByUsername(String username) { /* * 根据给定的用户名查询该用户信息,若没有记录则直接返回NULL,若查询到,将该条记录各个字段的值取出来存入到一个UserInfo实例中并返回。 */ Connection conn = null; try { conn = DBUtil.getConnection(); String sql = "SELECT id,username,password,nickname,account " + "FROM userinfo " + "WHERE username=?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, username); ResultSet rs = ps.executeQuery(); if(rs.next()) { int id = rs.getInt("id"); /* * username因为上面参数ps.setString(1, username); * 已经定义过了,所以在这我们直接用就行了 */ username = rs.getString("username"); String password = rs.getString("password"); String nickname = rs.getString("nickname"); int account = rs.getInt("account"); UserInfo userInfo = new UserInfo(id, username, password, nickname, account); return userInfo; } } catch (Exception e) { e.printStackTrace(); }finally { DBUtil.closeConnection(conn); } return null; }
UserInfoDAO dao = new UserInfoDAO();
,这件事放验证该用户是否已经存在的上面,也就是说,我们上来就先这个,实例化好这个dao啊,然后呢,在这判断的时候,怎么做呢,首先,我们调用这个dao的findByUsername(username)
,然后呢,是不是会得到一个UserInfo的实例,是这样的吧,然后,我们可以直接if里判断,看它得到的值,是不是不等于null的,如果不等于null,说明什么啊,就有这人,是吧,有这人的话呢,那就什么呢,该用户已存在,否则呢,再做咱们之前,下面那个注册的那一大堆事,是这样的吧。if-else
里面,对吧,就把原来这个啊,注册的这些信息,把它全放到else当中啊,就是如果有这个人的话,就先干该用户已存在这件事,如果没有的话,我们再把它们都这个进行,设置到UserInfo实例,然后,进行save啊,那么,当然在这个地方,我们还可以先做个,做个页面出来吧,比如说,这是一个注册的提示的这一个页面,reg_info.html
啊,说什么呢,该用户已存在,是吧,比如这块可以别写失败啊,其实呢,在将来,你们在写程序的时候,也会有这个问题啊,就是一般,我们尽可能少用什么啊,失败呀,错误呀,这种,这种词语,就会给用户一种很不爽的这种感觉,就会潜移默化的,一般就提示那个,就对不起,您那个用户已经存在了,你也不能够告诉它,注册失败,是吧,这个什么,这个就有点生硬,哈哈,恩。forward("/reg_info.html",response);
,就跳这个页面就行了,然后呢, 试试啊,这个啊,我们就用这个来试试啊,就是fanchuanqi,密码123456,然后再这个,是吧,如果,咱们还是用这个帐号,一执行,这样的话,它就告诉你,该用户已存在了,是吧,如果没问题的话,我们可以,比如说再写一个吧,这样的话,就正常的话,是不是就可以注册成功,是这样的吧。package service; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import common.HttpContext; import core.HttpServlet; import dao.UserInfoDAO; import db.DBUtil; import entities.UserInfo; import http.HttpRequest; import http.HttpResponse; /** * 用来完成用户注册功能 * @author fhy * */ public class RegServlet extends HttpServlet { /* * 我们用这个方法来完成业务处理 */ public void service(HttpRequest request,HttpResponse response) { //此时重启服务器,客户端访问注册页面就可以在控制台显示“开始处理注册!” System.out.println("开始处理注册!");//打桩 /* * 将用户的注册信息按行写入到 * userinfo.txt文件中。 * 每行为一条用户的信息,格式: * username,password,nickname * 例如: * fanchuanqi,123456,fanfan * liucangsong,222333,cangcang * * userinfo.txt放在webapp目录下 */ //获取用户名,密码,昵称 String username = request.getParameter("username"); String password = request.getParameter("password"); String nickname = request.getParameter("nickname"); /* Connection conn = null; try { conn = DBUtil.getConnection(); //先判断该用户是否存在 Boolean isNamed = comparedName(conn, username); if(isNamed||"nothing".equals(username)||"nothing".equals(password)||"nothing".equals(nickname)) { System.out.println("用户名已存在,或输入为空!"); forward("/reg_info.html", response); } else { Statement state = conn.createStatement(); String sql = "INSERT INTO userinfo " + "(id,username,password,nickname,account) " + "VALUES " + "(seq_userinfo_id.NEXTVAL,'"+username+"','"+password+"','"+nickname+"',5000)"; int d = state.executeUpdate(sql); if(d>0) { System.out.println("注册成功!"); */ UserInfoDAO dao = new UserInfoDAO(); //先验证该用户是否已经存在? if(dao.findByUsername(username)!=null||"nothing".equals(username)||"nothing".equals(password)||"nothing".equals(nickname)) { //该用户已存在! try { forward("/reg_info.html", response); } catch (Exception e) { e.printStackTrace(); } }else { //创建一个UserInfo实例用于表示该注册信息 UserInfo userInfo = new UserInfo(); userInfo.setUsername(username); userInfo.setPassword(password); userInfo.setNickname(nickname); userInfo.setAccount(5000); //保存用户信息 boolean success = dao.save(userInfo); try { if(success) { /* * 响应注册成功的页面给客户端 */ forward("/reg_ok.html", response); }else { //跳转注册失败页面 System.out.println("数据库插入失败!"); forward("/reg_info.html", response); } } catch(Exception e) { e.printStackTrace(); } } /* } } catch (Exception e) { //如果有异常的话,将异常输出一下。 e.printStackTrace(); }finally { DBUtil.closeConnection(conn); //closeBrAndPw(br,pw); } */ } }
public UserInfo findByUsernameAndPassword(String username, String password) {}
,同样如果要是能查着的话,是不是就把UserInfo对象给你,没有的话就返回null,明白吧,那就是说,你这输入用户名,密码对不对啊,只要有一个不对,那我也给你返回成null,总之就是有用户就传,没有用户就说明什么啊,这是根据给定的用户名和密码查询该用户。String sql = "SELECT id,username,password,nickname,account "
+ "FROM userinfo "
+ "WHERE username=? AND password=?";
SELECT id,username,password,nickname,sccount
,然后呢,FROM userinfo
,然后,WHERE username=? AND password=?
,是吧,然后呢,创建PreparedStatement,是吧,它等于,conn连接点prepareStatement(sql)
,把sql语句传进去,然后呢,ps.setString(1,username)
,第1个就是username,然后setString,第2个就是password,是这样的吧,然后呢,执行executeQuery,返回ResultSet,然后呢,if(rs.next()){}
,有,就是findByUsername方法中的if(rs.next()){}
里的那段,是吧,那我们就这个啊,得到这个用户名密码,什么这一大堆,是不是就生成一个UserInfo实例,传回去就行了。return null;
,就完了是吧,然后在登录这面怎么干啊,在LoginServlet中,那么,看你那个用户名密码拿到了以后呢,原来咱们是不是就得连数据库了,是吧,现在这些事不用干,是吧,那咱们怎么做,咱们可以比如说,拿到以后呢,创建这个UserInfoDAO是吧,new一个UserInfoDAO()
,然后呢,它要它的findByUsernameAndPassword(username,password)
,是吧,这样的话,是不是就可以得到了这个UserInfo的实例是吧,同样的,那个如果if(userinfo!=null){}
,就说明服务器有这个人的信息了,登录成功,实际上将来,你们还会把它,放到这个会话里,就是这个session里面,作为这个用户的登录凭证啊,那个等后面用到这个,实际咱们使用Tomcat的时候再说了啊。reg.html
是吧,就把那个页面,我们复制一份,我们再给它粘过来啊,粘一个,比如咱们叫作,update.html
,复制你那个注册页面,那里不是信息多一点么,然后呢,这个地方我们就写成修改用户,h1,我们说叫什么呢,用户修改,然后,这个form表单呢,咱们提交的时候,是不是这个action,这个得变一下,是不是咱们换成update啊,然后呢,这有用户名,密码,昵称,然后呢,咱再加一项啊,就是那个余额是吧,name="account"
,然后底下这块呢,我们提交按钮呢,我们写成修改啊,如果用JS的话,你们才会知道,这修改咱不能说点一下,啪,就提交过去了,点一下一般会怎么样,提示一个,确定修改么,是,再提交过去,那将来再做啊。localhost:8080/update.html
,就进到这个页面,这现在要真的点修改的话,它是不是给你跳到这个,没有这个页面来,因为我们是不是要到update这块来了,是吧,我现在没有update这个响应提示的页面啊,而且大家注意,你这个地方是不是正常情况下,你看,咱们这块控制台,也出了这么一个问题啊,之前就有同学就遇到这个事了,就是你们现在如果直接点击,这块有没有见到,这个ArrayIndexOutOfBoundsException,告诉你什么呀,数组下标越界了,是吧,原因在于什么呢,你看,咱们如果直接提交的话,就相当于你这输入框里什么都没输,是这样的吧,什么都没有输入的话,大家注意。username=&password=&nickname=&account=
,按照&拆完了以后,那就是相当于,拆出来每一个是username=
,password=
。username=
,这一个为例了,那它现在是不是长这样,那你想想,我们要是按照等号,拆开的话,是不是就相当于左面的拿到,右边什么都没有,对吧,所以就是说,说白了,你这数组只有一项,大家注意,你们记不记得我跟你们说过,如果咱们这按照逗号拆,比如我连续有两个逗号,中间是不是会拆一个空字符串,我记得我跟你们说过这个事,我说惟独这个连续的逗号要放到末尾,拆完这些空字符串,我后面的逗号就都不要了,有说过这么个事吧,所以就相当于你这个要是末尾出现等号呢,也就是后面本身应该能拆出一个空字符串,但那个就不要了,那一项,那等于就只剩下一项了,对吧。this.parameters.put(paraData[0], paraData[1]);
,这块写死的,就是两个的,左边的和右边的,另一项paraData[1]
是不是不一定有,对不对,那在这我们应该怎么办啊,至少是不是应该先做一个判断,首先判断什么呢,你拆出来的这个数组,它的length首先是不是得大于0,if(paraData.length>0) {}
,大于0,是不是说明至少你是拆出东西来了,对么,拆出东西来以后呢,那是不是还有两种情况,第一种情况,是你拆出的到底是一项,还是两项,对吧,所以在if(paraData.length>0) {}
里面呢,我们可以再进行if判断,if(paraData.length==1) {}
,就是什么呢,看看它的length,是等于1的么,如果等于1的话,说明什么,就相当于是不是就是username=
这个样子的,只有左边,没有右边。this.parameters.put(paraData[0], null);
,明白吧(这里实际代码里放了nothing,this.parameters.put(paraData[0], "nothing");
,用于在后面业务中作为标识判空),就放个null进去,那么,否则的话呢,否则的话,是不是就应就这两项了,else,否则的话呢,我们就把0和1,都给它放进去,这个是不是没有问题,所以,大家可以按照我这个,咱把这个拆分呢,稍微改正一下啊。/reg
的,加过这个/login
的么,是吧,也就是说,咱们在这,还要再补一个,补一个什么呢,之前咱们这个udate.html
页面的,这个请求的form表单访问的是update,是吧。/update
吧,是吧 ,如果是update的话,我们这块访问的是那个UpdateServlet啊,加这个就行啊,那么这个写好了以后呢,那咱们在这个UpdateServlet里面,是不是就可以开始,比如你通过request,是不是可以先获取用户输入的用户名啊,密码啊,昵称啊,account那几个值,是吧,但是唯一需要注意的是,account,这个拿回来的可是个字符串,就是咱们这块页面上,你这个页面上,就算你输入的是数字,你这拿到的也是字符串,你是不是要先给它转成int值,是这样的吧,然后,变成一个UserInfo的实例,是不是调我们的这个DAO的update的方法,就把这个对象给进去,然后呢,这个DAO呢,就按照你这个对象里面,新的样子,给它进行修改啊。public boolean update(UserInfo userInfo) {}
,就是说,将来这个业务层,把这个UserInfo给我们以后,这个里面是不是就记录了用户想改的内容,然后,前提是它的名字是不能改的,所以在这不改名字,把这UserInfo里面,设置的密码啊,昵称啊,还有account,是不是可以换成你这个的值啊,然后条件就是按照用户名改就行了啊,如果改完你发现影响表中,一条记录,那就修改成功,return true,没改成功,return false;,就完事了啊。request.getParameters
,把修改信息的那几个用户名,密码等的,那几个值都拿到了,是吧,那都拿到以后,然后呢,这是第一步,先获取用户信息,那第2步呢,干嘛呢,是不是你得先看看,他是不是这个用户名,有没有这个人,是吧,所以这个干嘛呢,第二步干嘛啊,先判断该用户是否存在,是吧,不存在,则提示用户,就是你跳一个页面啊,没有该用户的提示页面。/** * 根据给定的用户名和密码查询该用户 * @param username * @param password * @return */ public UserInfo findByUsernameAndPassword(String username, String password) { Connection conn = null; try { conn = DBUtil.getConnection(); String sql = "SELECT id,username,password,nickname,account " + "FROM userinfo " + "WHERE username=? AND password=?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1,username); ps.setString(2, password); ResultSet rs = ps.executeQuery(); if(rs.next()) { int id = rs.getInt("id"); username = rs.getString("username"); password = rs.getString("password"); String nickname = rs.getString("nickname"); int account = rs.getInt("account"); UserInfo userInfo = new UserInfo(id, username, password, nickname, account); return userInfo; } } catch (Exception e) { e.printStackTrace(); } finally { DBUtil.closeConnection(conn); } return null; }
package service; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import core.HttpServlet; import dao.UserInfoDAO; import db.DBUtil; import entities.UserInfo; import http.HttpRequest; import http.HttpResponse; public class LoginServlet extends HttpServlet{ public void service(HttpRequest request,HttpResponse response) { System.out.println("开始登录...");//打桩 //获取用户名,密码 String username = request.getParameter("username"); String password = request.getParameter("password"); System.out.println(username+","+password);//打桩 /* * 读取userinfo.txt文件中的所有用户,并且逐个比较。 * 先现实功能,然后在考虑其他事,直截了当的话,首先你这读文件了,try-catch捕获异常是在所难免的。 */ //先创建一个BufferedReader,按行读就可以了,当然,这个BufferedReader用完了得关掉,所以把它放到外面定义了。 //BufferedReader br = null; Connection conn = null; try { //基础工作做好后,在这里初始化br,new转换流,new字节流,传入要读取得文件。 //br = new BufferedReader(new InputStreamReader(new FileInputStream("webapp"+File.separator+"userinfo.txt"))); // conn = DBUtil.getConnection(); //Boolean flag = comparedNameAndPwd(br,username,password); // Boolean flag = comparedNameAndPwd(conn,username,password); //flag的值判断完成后,我们就可以在这根据它的值是否为true来决定它是否登录成功。 UserInfoDAO dao = new UserInfoDAO(); UserInfo userInfo = dao.findByUsernameAndPassword(username, password); if(userInfo!=null) { // if(flag) { System.out.println("登录成功!");//打桩 /* * 响应登录成功的页面给客户端(拷贝上面代码,登录失败也一样) */ forward("/login_ok.html",response); } else { System.out.println("登录失败!"); forward("/login_error.html", response); } } catch (Exception e) { e.printStackTrace(); }/*finally { DBUtil.closeConnection(conn); //closeBr(br); }*/ } private void closeBr(BufferedReader br) { //先把基础的工作都写好,在这里把BufferedReader关掉。 if(br!=null) { try { //关流时,它要求捕获异常,我们就捕获异常。 br.close(); } catch (IOException e) { e.printStackTrace(); } } } private Boolean comparedNameAndPwd(Connection conn, String username, String password) throws SQLException { boolean flag = false; String sql = "SELECT id,username,password,nickname,account " + "FROM userinfo " + "WHERE username=? AND password=? "; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, password); ResultSet rs = ps.executeQuery(); if(rs.next()) { flag = true; } return flag; } private Boolean comparedNameAndPwd(BufferedReader br, String username, String password) throws IOException { //然后,按行读取文件的内容,先从文件里读取一行,如果读取到的内容不为空,就说明我读到一行了。 String line = null; boolean flag = false;//登录是否成功 while((line = br.readLine())!=null) { System.out.println("line:"+line);//打桩 //按照","拆分出用户名和秘密 String[] userInfos = line.split(","); /* * 判断用户名和秘密是否一致 */ if("".equals(username.trim())||"".equals(password.trim())) { flag=false; } if(username.equals(userInfos[0])&&password.equals(userInfos[1])) { //登录成功! 将flag改成true,即这样逐行判断,只要有一个对了,就将flag改成true,之后就break,后面的就不用再读取判断了。 flag = true; } } return flag; } }
<html> <head> <meta charset="UTF-8"> <title>修改用户</title> </head> <body> <center> <h1>用户修改</h1> <form action="update" method="get"> <table border="1"> <tr> <td>用户名:</td> <td><input name="username" type="text" size="30"/></td> </tr> <tr> <td>密 码:</td> <td><input name="password" type="password" size="30"/></td> </tr> <tr> <td>昵 称:</td> <td><input name="nickname" type="text" size="30"/></td> </tr> <tr> <td>余 额:</td> <td><input name="account" type="text" size="30"/></td> </tr> <tr> <td colspan="2" align="center"><input type="submit" value="修改"></td> </tr> </table> </form> </center> </body> </html>
/** * 修改给定的用户信息 * @param userInfo * @return */ public boolean update(UserInfo userInfo) { /* * 名字和ID不可修改,可以根据用户名 * 修改usserInfo中该用户的新密码,昵称 * 以及余额 * UPATE userinfo * SET password=?,nickname=?,account=? * WHERE username=? */ Connection conn = null; try { conn = DBUtil.getConnection(); String sql = "UPDATE userinfo SET " + "password=?,nickname=?,account=? " + "WHERE " + "username=?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, userInfo.getPassword()); ps.setString(2, userInfo.getNickname()); ps.setInt(3, userInfo.getAccount()); ps.setString(4, userInfo.getUsername()); int d = ps.executeUpdate(); if(d>0) { //修改数据成功 return true; } } catch (Exception e) { e.printStackTrace(); } finally { DBUtil.closeConnection(conn); } return false; }
<html>
<head>
<meta charset="UTF-8">
<title>不存在</title>
</head>
<body>
<center>
<h1>修改失败!用户名不存在或错误的输入!</h1>
</center>
</body>
</html>
<html>
<head>
<meta charset="UTF-8">
<title>成功</title>
</head>
<body>
<center>
<h1>修改成功!</h1>
</center>
</body>
</html>
package service; import core.HttpServlet; import dao.UserInfoDAO; import entities.UserInfo; import http.HttpRequest; import http.HttpResponse; /** * 完成用户修改信息的请求 * @author soft01 * */ public class UpdateServlet extends HttpServlet{ public void service(HttpRequest request,HttpResponse response) { /* * 1:获取用户输入的用户名等信息 * 2:先判断该用户是否存在,不存在则 * 提示用户(跳转没有该用户的提示页面) * 3:将用户输入的信息存入一个UserInfo * 实例中 * 4:调用UserInfoDAO的update进行修改 * 5:若修改成功跳转修改成功的提示页面 * 否则跳转失败的提示页面 */ //获取用户名,密码,昵称 String username = request.getParameter("username"); String password = request.getParameter("password"); String nickname = request.getParameter("nickname"); String account = request.getParameter("account"); try { Integer.valueOf(account.trim()); } catch(NumberFormatException e) { e.printStackTrace(); System.out.println("accout转型失败!"); try { forward("/update_info.html", response); } catch (Exception e1) { e1.printStackTrace(); } } UserInfoDAO dao = new UserInfoDAO(); //先验证该用户是否不存在? if(dao.findByUsername(username)==null||"nothing".equals(username)||"nothing".equals(password)||"nothing".equals(nickname)) { //该用户不存在! try { forward("/update_info.html", response); } catch (Exception e) { e.printStackTrace(); } }else { //创建一个UserInfo实例用于表示该注册信息 UserInfo userInfo = new UserInfo(); userInfo.setUsername(username); userInfo.setPassword(password); userInfo.setNickname(nickname); userInfo.setAccount(Integer.parseInt(account.trim())); //保存用户信息 boolean success = dao.update(userInfo); try { if(success) { /* * 响应修改成功的页面给客户端 */ forward("/update_ok.html", response); }else { //跳转注册失败页面 System.out.println("数据库插入失败!"); forward("/update_info.html", response); } } catch(Exception e) { e.printStackTrace(); } } } }
package core; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import common.HttpContext; import http.HttpRequest; import http.HttpResponse; import service.LoginServlet; import service.RegServlet; import service.UpdateServlet; /** * 该线程任务用于处理每个客户端的请求。 * @author fhy * */ public class ClientHandler implements Runnable { private Socket socket; public ClientHandler(Socket socket) { this.socket = socket; } public void run() { try { //System.out.println("进入到run方法!"); InputStream in = socket.getInputStream(); //创建对应的请求对象 //System.out.println("已经从socket中获取到输入流in:"+in); HttpRequest request = new HttpRequest(in); OutputStream out = socket.getOutputStream(); //创建对应的响应对象 HttpResponse response = new HttpResponse(out); /* * 处理用户请求 * 0.获取用户请求资源路径:/index.html */ String uri = request.getUri(); System.out.println("uri:webapp"+uri); //正常我们访问一个网站,只敲一个域名(不需要写资源路径), //比如www.baidu.com,它就会自动跳转到首页,我们也这样做。 if("/".equals(uri)) { //去首页 File file = new File("webapp/index.html"); responseFile(HttpContext.STATUS_CODE_OK,file,response); }else { File file = new File("webapp"+uri); if(file.exists()) { System.out.println("找到了相应资源"+file.length()); /* * 响应页面要向用户发送的内容: * HTTP/1.1 200 OKCRLF * Content-Type:text/htmlCRLF * Content-Type:273CRLF * CRLF * 1010100101001011010101010111110010010(index.html数据) */ responseFile(HttpContext.STATUS_CODE_OK,file,response); //查看是否请求一个功能,先看它请求是不是一个注册if("/reg".equals(uri)),如果要是注册的话就要先办法把用户注册信息写到文件里。 } else if("/reg".equals(uri)) { //调RegServlet类中的service方法,让它去处理这个请求。首先第一件事,得先实例化出来RegServlet这个类的对象。 RegServlet servlet = new RegServlet(); //然后调用它的service方法,并把request对象和请求对象response传给这个方法作为参数。 servlet.service(request,response); } else if("/login".equals(uri)) { //调LoginServlet类中的service方法,让它去处理这个请求。首先第一件事,得先实例化出来LoginServlet这个类的对象。 LoginServlet servlet = new LoginServlet(); //然后调用它的service方法,并把request对象和请求对象response传给这个方法作为参数。 servlet.service(request,response); } else if("/update".equals(uri)) { UpdateServlet servlet = new UpdateServlet(); servlet.service(request,response); } else { System.out.println("没有资源:404"); file = new File("webapp/404.html"); responseFile(HttpContext.STATUS_CODE_NOTFOUND,file,response); } } } catch (Exception e) { e.printStackTrace(); } finally { try { //如果不关流logo图片将出不来。 socket.close(); }catch (Exception e) { e.printStackTrace(); } } } /** * 根据给定的文件分析其名字后缀以获取对应的ContentType * @param file * @return */ private String getContentTypeByFile(File file) { //获取文件名 String name = file.getName(); System.out.println("文件名:"+name);//打桩 //截取后缀 String ext = name.substring(name.lastIndexOf(".")+1); System.out.println("后缀:"+ext);//打桩 //获取对应的ContentType String contentType = HttpContext.contentTypeMapping .get(ext); System.out.println("contentType:"+contentType); return contentType; } /** * 相应客户端指定资源 * @param status 响应状态码 * @param file 要响应的资源 * @throws Exception */ private void responseFile(int status, File file,HttpResponse response) throws Exception { try { //1 设置状态行信息 response.setStatus(status); //2 设置响应头信息 //分析该文件后缀,根据后缀获取对应的ContentType response.setContentType(getContentTypeByFile(file)); response.setContentLength((int)file.length()); //3 设置响应正文 response.setEntity(file); //4 响应客户端 response.flush(); } catch (Exception e) { throw e; } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。