12月5日——培训第14天

来源:互联网 发布:机甲风暴max破解版java 编辑:程序博客网 时间:2024/05/07 17:50

今天改坐了307,虽然车挤了一些,但是确实十分的顺利,没有一点堵车,不错。
今天该是行集rowset了吧,也不知道这netMeeting能不能用了。
---------------------------------------
netMeeting还是不行,拨上一段时间就掉线,好多人拨不上去
没办法,只好用投影仪了……

讲解昨天的作业:

if(rs.next())
{
 System.out.println("Welcome!");
 System.out.println("请您输入新的密码");
 //调用自己定义的readString方法来读入一行控制台的字符串
 //也就是:
 BfferedReader reader = new BufferedReader(new InputStreamReader(System.in));
 //千万注意!这个reader最后也是要和结果集它们一样放到finally中关闭!

 //InputStreamReader是将字节流转换为字符流。
 String newPw = reader.readLine();
 //结果集中有两类方法:getter方法:get+数据类型
     //update方法:update+数据类型
 rs.updateString("password",newPw);//这句话是更改密码为newPw变量字符串的值,注意前面要设置好结果集的类型!!
         //注意这里的updateString也和preparedStatement一样具有过滤作用
         //也就是防止昨天说的sql注入安全隐患。
 //记住要设置结果集的参数:stmt = conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,
             // ResultSet.CONCUR_UPDATABLE)
 rs.updateRow();//这句话如果不加的话,不会因为更改了结果集而使对应的数据库发生变化

 //这时如果我想要利用结果集来插入新的用户的话,则还是利用上面的方法读入控制台中的一行字符串,
 String newUserPw = reader.readLine(); //注意这里有一些代码我省略了

 //注意下面使用结果集来插入数据库的步骤!!!
 //如果要插入的话首先要将游标移动到可插入记录的位置
 rs.moveToInsertRow();
 //其次,修改插入记录的位置上的各个字段值
 rs.updateString("username",newUser);
 rs.updateString("password",newUserPw);
 rs.updateTimestamp("longTime",new Timestamp(System.currentTimeMillis()));//这里也可以用Calendar里面的getTimeMillis方法
 //第三,插入记录
 rs.insertRow();

 //最后,再将游标移回到原来的位置上。
 rs.moveToCurrentRow();要不移回游标的话,游标还是在空行那里。
 System.out.println("insert successfully!");

-------------------------------------------------
 //既然上面使用结果集更新记录以及插入记录的代码都已经提供出来了,现在还剩下用结果集来删除数据库中的记录,要怎么作呢??
 注意下面的实行步骤!!
 //首先,将游标移动到要删除记录的位置!
 System.out.println("您要删除第几行??");
 String num = reader.readLine();
 rs.absolute(Integer.parseInt(num));

 //第二,删除记录,注意无须指明条件!这个方法是一次删除一条记录
 rs.deleteRow();
 System.out.println("删除成功!!");
 
 
 //最后别忘了关闭reader:
 try
 {
  if(reader!=null)
  {
   reader.close();
   reader = null ;
  }
 }
 catch(IOException ex)
 {
  ex.printStackTrace();
 }
}
//还有一个问题很重要,Connection中的getMetaData方法可以得到DatabaseMetaData对象,
//这个对象有一些support的方法来判断,结果集的类型组合是否被驱动所支持,或者事务的隔离
//级别是不是被这个驱动所支持。


//代码的问题是从开始到结尾连接一直都是保持着的!这也是结果集更新数据库的最大问题,
//因为正常情况下,一个连接被占用的时间不可以超过几秒钟!
-------------------------------------------------------------
刚才一个管理机房的老太太来调了几下,竟然把netmeeting弄好用了,汗啊……毕竟刚才老师都搞不定呢
现在是课间休息时间:如何解决上面所说的连接一直被占用的问题呢??
----------------------------------
课间休息结束:

直接用结果集来更新的弊病:一直占用连接资源

有没有一种结果集可以不占用连接资源就对数据库进行更新呢?有的,是离线结果集。
它只有在更改数据库的时候才去获取连接,而不是一直占用资源。离线结果集也叫做行集。

行集可以脱离数据库独立存在,结果集不可以脱离数据库。行集RowSet在javax.sql包中
而且jdk1.4不具体支持RowSet,只是提供个接口而已……

但是即便是在jdk1.5中也没有RowSet的实现类。
oracle驱动中的ocrs12(oracleRowset)有rowset的实现类。sun公司也有提供rowSet实现类的下载!

行集实现要求必须扩展BaseRowSet,至少实现一种RowSet接口。
子接口:CachedRowSet,FilteredRowSet,JdbcRowSet,JoinRowSet,WebRowSet
我们主要讲JdbcRowSet和CachedRowSet

JdbcRowSet也需要像结果集一样维持一个和数据库的连接,CachedRowSet是真正意义上的结果集,
FilteredRowSet有过滤作用,JoinRowSet(mysql和oracle都不支持它)支持表的连接,
WebRowSet支持和xml文档之间的数据交互。

对于我们来说,CachedRowSet是最有用处的。

下面是JdbcRowSet的使用例子
-------------------------------------
注意使用行集要导入行集的jar包RowSet.jar
public class Demo
{
 public static void main(String[] args)
 {
  Connection conn = null ;
  PreparedStatement pstmt = null ;
  ResultSet rs = null ;

  try
  {
   Class.forName("com.mysql.jdbc.Driver");
   conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/j2ee?user=root&password=root");

   pstmt = conn.prepareStatement("select * from users");
   rs = pstmt.executeQuery();
   //下面开始创建行集:
   //千万注意在创建行集之前一定要在工程属性里面的javaCompiler驱动里面把ComplianceLevel
   //从1.4改为5.0!要不是没有办法创建行集的!
   
   jrs = new JdbcRowSetImpl(rs);//如果结果集不支持某种结果集类型搭配的话,这样用行集包装了一下后可能就可以
          //支持那些结果集类型了,但是也不一定,还要看具体的驱动程序是不是支持。
   jrs.absolute(3); //行集默认情况下是可以更新的,而且游标也是可滚动的,对数据库底层更新是不敏感的
   System.out.println(jrs.getString(2));

   //当然我们也可以不借助包装结果集的方法来创建行集:(也就不需要创建数据库连接,数据库语句了,但是记住一定不能
   //省略Class.forName,否则不注册Driver的话是不可以加载驱动程序的,也就谈不上连接的问题了)
   jrs = new JdbcRowSetImpl();
   jrs.setUrl("jdbc:mysql:///j2ee?user=root&password=root");
   //上面的url等价于jdbc:mysql://localhost:3306/j2ee?user=root&password=root

   jrs.setCommand("select * from users where username=?");
   jrs.setString(1,"yuan");  //和PreparedStatement类似的方法预编译。
   jrs.execute();  //这就可以执行了

   jrs.absolute(2);//到第二行记录去。
   System.out.println(jrs.getString(2));
   //上面这是jdbcRowSet的应用,还是在线的。


  }
  catch(Exception ex)
  {
  }
 }
};

-----------------------
下面是CachedRowSet:
public class CachedDemo
{
 public static void main(String[] args)
 {
  CachedRowSet crs = null ;
  try
  {
   Class.forName("com.mysql.jdbc.Driver");
   crs = new CachedRowSetImpl();
   crs.setUrl("jdbc:mysql:///j2ee");
   crs.setUsername("root");
   crs.setPassword("root");
   crs.setCommand("select * from users");//在执行这句话的时候才连接数据库!   
   crs.execute(); //在执行完这条语句后,连接资源立刻被释放!也就是行集和数据库的连接断开
   
   //下面开始更新这个离线的行集。
   crs.absolute(1)//先将游标移动到指定的位置。
   crs.updateString("username","java");
   crs.updateString("password","java");
   crs.updateRow() //调用updateRow来将更改提交到数据库,但是必须要调用下面的语句才能真正更改数据库!!!
   //只有在执行acceptChanges方法时,才去连接数据库
   crs.acceptChanges();//千万别落下这句话!在执行完这句话后,就清除了所有的资源
  }
  catch(SQLException ex)
  {
   e.printStackTrace();
  }
 }
};
----------------------------------
中午张老师来讲了一下指针和字符串常量的问题,一切都源于邢啸屹的一个关于字符串常量的问题,
真没想到一个问题能引出这么多来,现在还在继续延续展开,连C语言的东东都出来了,好家伙……中午的休息好像又泡汤了……

张老师讲东西太天马行空了,这里一处那里一点的,我这里笔记也很难整理,也就是不系统,这方面我感觉田老师确实做的比较好
-----------------------------------

假如一个指针p
delete *p 语句到底干什么了呢?很简单,它只是把p指针指向的那块内存标为可以让别的指针使用,至于那块内存里面的内容
是没有变化的!这之后要把p置为null值,因为一旦执行了delete后,这块指定好的内存就可以被别的指针修改了,如果没有
执行p=null的话,p还是可以访问这块内存,但是内存可能已经被别的指针改变了……
所以在delete *p后一定要执行p=null

内存泄漏:p = new char[100]之后申请了一块内存给p,假如在没有执行delete语句的时候又执行了一遍
p = new char[100]的话,第一块内存仍然是不可以被别的指针引用的!(因为没有执行delete语句)
这样第一快内存就被浪费了,这就是内存泄漏。

C++中只有堆中的内存对应的指针才需要释放。
假如*p = null;
那么delete p不会报错。
---------------------------------------
---------------------------------------
public class CachedDemo
{
 public static void main(String[] args)
 {
  CachedRowSet crs = null ;
  try
  {
   Class.forName("com.mysql.jdbc.Driver");
   crs = new CachedRowSetImpl();
   crs.setUrl("jdbc:mysql:///j2ee");
   crs.setUsername("root");
   crs.setPassword("root");
   crs.setCommand("select * from users");//在执行这句话的时候才连接数据库!   
   crs.execute(); //在执行完这条语句后,连接资源立刻被释放!也就是行集和数据库的连接断开
   
   //下面开始更新这个离线的行集。
   crs.absolute(1)//先将游标移动到指定的位置。
   crs.updateString("username","java");
   crs.updateString("password","java");
   crs.updateRow() //调用updateRow来将更改提交到数据库,但是必须要调用下面的语句才能真正更改数据库!!!
   //只有在执行acceptChanges方法时,才去连接数据库
   Thread.sleep(15000);//让程序暂时停止15秒,如果这时我们更改了数据库中相应记录的话会如何呢?
        //结果是会抛出异常的……
   crs.acceptChanges();//千万别落下这句话!在执行完这句话后,就清除了所有的资源
  }
  catch(SyncProviderException spe) //如果发生了冲突的话,执行下面的语句。
  {
   try
   {
    SyncResolver sr = spe.getSyncResolver();//sr是发生冲突的行集。

    while(sr.nextConflict())//得到下一个发生冲突的行
    {
    if(sr.getStatus()==SyncResolver.UPDATE_ROW_CONFLICT)//如果是更新的冲突
    {
     int rowNum = sr.getRow();//得到发生冲突的行
     crs.absolute(rowNum);  //游标移动到发生冲突的该行,也就是让行集的游标也到那个发生冲突的行去
     int cols = crs.getMetaData().getColumnCount();//还有一种方法getColumnName可以得到字段名字
     //上面是得到该行的列数目
     
     //在行集crs和冲突行集sr都移动到了同一行时,就该一个个的判断哪个列有冲突了。
     for(int i = 1 ; i <= cols ; i ++)
     {
      if(sr.getConflictValue(i)!=null){  //如果对应的某列确实发生了冲突,那么执行对应处理
       int conflictValue = Integer.parseInt(sr.getConflictValue(i).toString());//取出的是冲突值
       int ordinary = crs.getInt(i);//取出的是行值
       sr.setResolvedValue(i,conflictValue>ordinaryValue?conflict:ordinaryValue);
       //判断冲突值和行值哪个大哪个大就用哪个值解决冲突!
      }
     }
    }
    }

   }
   catch(SQLException ex)
   {
    ex.printStackTrace();
   }

   //上面这段catch代码问题很大,老师课上没有调通…………………………
  }
  catch(SQLException ex)
  {
   e.printStackTrace();
  }
 }
};

----------------------------------
行集的分页问题:
CachedRowSet中:
setPageSize(int size):每一页多少记录
nextPage();下一页:


public class CachedDemo
{
 public static void main(String[] args)
 {
  CachedRowSet crs = null ;
  try
  {
   Class.forName("com.mysql.jdbc.Driver");
   crs = new CachedRowSetImpl();
   crs.setUrl("jdbc:mysql:///j2ee");
   crs.setUsername("root");
   crs.setPassword("root");
   crs.setCommand("select * from users");//在执行这句话的时候才连接数据库!
   
   crs.setPageSize(1);//每页只有一条记录。

   crs.execute(); //这句话必须在分页方法执行完后再执行,要不然如果先执行了execute方法而不是分页方法的话
       //肯定起不到分页的效果。
   while(crs.nextPage())//每次都读出下一页的两条记录构成一个新的结果集!
   {
    System.out.println("a page flag:");
    while(crs.next())//现在行集中是当页的若干行
    {
     System.out.println(crs.getSring(2));
    }
    System.out.println();System.out.println();System.out.println();
    
   }
//select * from(select rownum as num, * from emp where rownum<?)
//where num >? 这是oracle中的分页。

//select * from users limit [n,]m ;
//limit 10    取前10行
//limit 2,10  从第3条记录开始取,取10个,也就是说取第3条到第12条记录

//select * from table limit(Curpage*10),10就是每页10条记录的分页显式了

//不同的数据库中分页是不同的,所以呢,使用crs的分页功能可以跨数据库。
  }
 }
}

------------------------------------
行集就介绍这么多,下面引入ORM的概念
ORM:Object Relationship Mapping 对象关系映射
它是EJB(实体Bean)、Hibernate、OJB等各种数据层框架的理论基础。

每个关系型的表完全可以和java中的一个具体类对应,每一条记录都要和一个对象对应起来
ORM的最高理想:只要更改了对象,那么表里数据也会变化;

也就是完全抛弃sql语句,完全对对象进行操作,但是ORM到现在也没能做到,EJB是能够完全
做到一个对象的属性发生变化能反应到数据库表中的对应字段。

EJB把所有属性都设置为私有,创建的时候就从数据库里读取数值,然后利用setter方法里面
的事务更改数据库,达到一调用setter方法更改对象就更改了数据库;同时通过getter方法
取对象属性时也是要连接数据库去作的。

但是EJB运行起来太慢了,几乎没人用了。制定EJB3.0标准的时候,(今年5月8日通过的),
由于Hibernate的关系,所以EJB3.0的标准几乎和Hibernate相似。

Hibernate是数据层的一个比较完整的实现,ibatis也是其中一个。
-----------------------------
一个简单ORM映射的实现:
import java.sql.Timestamp;

public class User
{
 private int id ;
 private String username;
 private String password ;
 private int longTime;
 private Timestamp loginTime ;

 然后利用eclipse开发工具快速添加getter和setter方法

 //对于orm来说,实体类的equals方法是必须实现的!!!
 public boolean equals(Object obj)
 {
  //这段代码以前写过了
 }

 //只要写了equals方法,就一定要重写hashCode方法,具体方法及原因见以前的日记!
};

//另一个类:
public class Demo
{
 Connection con = null ;
 PreparedStatement pstmt = null ;
 ResultSet rs = null ;

 Class.forName(……);
 conn = DriverManager.getConnection(……);
 
 String sql = select * from users ;
 pstmt = conn.prepareStatement(sql);
 rs = pstmt.executeQuery();
 
 List list = new ArrayList();
 while(rs.next())
 {
  User u = new User(); //这句话绝对不可以放到外面去!!否则list中得到的u对象集合属性全部都是最后一个记录的值!
       //相当于每一次都是更改了那一个对象,以最后一次更改的为准,结果add的都是同一个对象,最后的值肯定
       //都一样,也就是最后一条记录的值。
  u.setId(rs.getInt(1));
  u.setUsername(rs.getString(2));
  u.setPassword(rs.getString(3));
  u.setLoginTimes(rs.getInt(4));
  u.setLoginTime(rs.getTimestamp(5));
  
  list.add(u); //这样退出后就得到一个list对象的集合
 }

 Iterator it = users.iterator();
 while(it.hasNext())
 {
  User u1 = (User)it.next();
  System.out.println(u1.getUsername());
 }
};

-------------------------------------

今日重点:
1 结果集的三种属性:并发行,有效性和可滚动性,如何使用结果集对数据库去进行更新、删除、插入。
  每一个人都能够使用Statement和PreparedStatement和ResultSet来对数据库进行增删改查。
2 什么是行集?为什么要使用离线结果集(行集)?也就是offline rowset。能够使用RowSet对数据库
  进行增删改查,如果能够自己实现一个离线行集的话当然就相当不错了。
3 知道什么是ORM,如何实现ORM,能够自己写出一个简单的ORM映射程序。

JDBC总结大纲,知识概要:
1 驱动程序类型从连接上分有哪些?异同?
2 获取连接方式:DriverManager和DataSource两种。
3 JDBC由连接生成的语句有哪几种?分别有什么用途?在连接关闭后,语句是否还可以使用??
4 掌握结果集的三类方法:
(1)移动游标的方法
(2)getter方法(getString、getInt……)
(3)update方法(updateString、updateInt……)
5 结果集的属性有哪些?如何设置?
6 行集的作用,为什么要使用行集?
7 ORM含义,能写出简单的ORM程序
8 了解连接池概念及其与DataSource的关系。(以后还会详细的介绍连接池)

最终能够独立的完整的实现对数据库表的增、删、改、查。并保证不出现内存泄漏。(及时关闭连接、语句和结果集就可以防止内存泄漏)

练习:
1 使用行集实现修改用户名、密码和插入一条新的记录。
2 简单的ORM程序实现。

----------------------------------------------

今天是周二,所以我晚上就不在这里的机房呆了,今天要回去,明天放假。 

原创粉丝点击