Java EE笔记 (1) JDBC

JDBC(全称为:Java Data Base Connectivity)
共两个包:
java.sql
javax.sql
工做流程:
应用程序—JDBC—MySQL驱动或Oracle驱动—MySQL数据库或Oracle数据库
MySQL驱动:mysql-connector-java-5.0.8-bin.jar
Oracle驱动:ojdbc14.jar

1、JDBC工做六大步骤:
1.加载驱动
DriverManager.registerDriver("com.mysql.jdbc.Driver") 
不推荐,这种形式会形成JVM虚似机内存中产生两个同样的Driver对象。
Class.forName("com.mysql.jdbc.Driver");
推荐这种方式,不会对具体的驱动类产生依赖。

2.创建链接
Connection conn = DriverManager.getConnection(url,user,pass); //user与pass为登录数据库的用户名与密码
也可采用这种方式
Connection conn = DriverManager.getConnection(url?user=root&&password=root); //用户名与密码以参数带在后面
协议:子协议://主机:端口/数据库名
MySQL的url:jdbc:mysql://localhost:3306/数据库名
Oracle的url:jdbc:oracle:thin:@localhost:1521:数据库名
SQLServer的url:jdbc:microsoft:sqlserver//localhost:1433; DatabaseName=数据库名

3.建立执行SQL的语句
(1)采用Statement实现,有被SQL注入的危险,如:('or 1=1 or name=') ,且可能形成数据库缓冲区溢出:
Statement st = conn.createStatement();

(2)采用PreparedStatement实现,此类安全,可对SQL进行预编译:
String sql = "insert into 表名(id,name,birthday,mytext,myblob) values(?,?,?,?,?);
PreparedStatement st = conn.prepareStatement(sql);

conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);//该常量指示生成的键能够获取。
st.setInt(1,1);  //对应第1个?号
st.setString(2,"wudi");  //对应第2个?号
//对应第3个?号,日期存入数据库时要转成sql类型的Date,getTime()方法将日期转成毫秒数字
st.setDate(3, new java.sql.Date(new java.util.Date().getTime()));  
//对应第4个?号,存大文本
URL url = 类.class.getClassLoader().getResource("a.txt");//获得文件虚拟地址
File file = new File(url.getPath()); //获得文件真实路径并用file链接
st.setCharacterStream(4, new FileReader(file), (int) file.length());
//对应第5个?号,存二进制文件
URL url = 类.class.getClassLoader().getResource("a.jpg");//获得文件虚拟地址
File file = new File(url.getPath()); //获得文件真实路径并用file链接
st.setBinaryStream(2, new FileInputStream(file), (int) file.length());  //MySql数据库存的方式

4.执行语句
ResultSet rs = st.getGeneratedKeys();  //获得插入行的主键,只对insert有用
ResultSet rs = st.excute(sql);       //任意操做
ResultSet rs = st.excuteQuery(sql);  //查询时
ResultSet rs = st.excuteUpdate(sql); //增删改时
//可实现批处理sql语句
Statement.addBatch(sql)  
优势:能够向数据库发送多条不一样的sql语句。
缺点:sql语句没有预编译
PreparedStatement.addBatch(sql)
优势:发送的是预编译后的SQL语句,执行效率高。
缺点:只能应用在sql语句相同,但参数不一样的批处理中。

(1)小例子:采用Preparedstatement.addBatch(sql)方法实现sql语句批处理,
String sql = "insert into tbatch(id,name,password) values(?,?,?)";
st = conn.prepareStatement(sql);
for(int i=1;i<500003;i++){
  st.setInt(1, i);
  st.setString(2, "aa" + i);
  st.setString(3, "1111" + i);
  //程序执行到此,st对象里面已是一条完整的sql,能够加到 batch里面去了
  st.addBatch();
  if(i%1000==0){//batch最大可容1000条sql语句
      st.executeBatch();  //执行来1次batch里的全部sql,
      st.clearBatch();  //清空batch里的全部sql
  }
}

(2)小例子:在Oracle数据库中实现存二进制文件,注意:这些操做需开启事务。
//1.向列中存一个指针
String sql = "insert into tblob(id,image) values(1,empty_blob())";
PreparedStatement st = conn.prepareStatement(sql);
st.executeUpdate();
//2.把指针查询出来
sql = "select 二制文件列名 from 表 where id=1 for update";  //for update是为了锁定这一行,为了避免产生并发冲突
st = conn.prepareStatement(sql);
rs = st.executeQuery();
if(rs.next()){
  BLOB blob =  (BLOB) rs.getBlob("image");
  OutputStream out = blob.getBinaryOutputStream();
  URL url = 类.class.getClassLoader().getResource("a.jpg");//获得文件虚拟地址
  File file = new File(url.getPath()); //获得文件真实路径并用file链接
  FileInputStream in = new FileInputStream(file);
  byte buffer[] = new byte[1024];
  int len = 0;
  while((len=in.read(buffer))>0){
     out.write(buffer, 0, len);
  }
}
st.executeBatch();

5.处理执行结果
rs.Previous()://移动到前一行
rs.absolute(1)://移动到指定行
rs.beforeFirst():移动ResultSet的最前面(表头)
rs.afterLast() ://移动到ResultSet的最后面(表尾)
if(rs.next){    //指向第一条,下次执行指向第二行
第一种方法,获得的是个Object对象
   rs.getObject("id");   //取出该行列名为id的值
   rs.getObject(1);  //取出该行第一列的值
第二种方法,获得的是对应类型的值
   rs.getBoolean()       //BIT(数据库中对应的类型)
   rs.getByte()          //TINYINT
   rs.getShort()         //SMALLINT
   rs.getInt()           //int
   rs.getLong()          //BIGINT
   rs.getString()        //CHAR,VARCHAR,LONGVARCHAR
   //用流方式取出数据库中的大文本 //Text,longText
   Reader reader = rs.getCharacterStream("大文本列名"或列号);
   或
   Reader reader = rs.getClob("大文本列名"或列号).getCharacterStream();
   char buffer[] = new char[1024];
   int len = 0;
   FileWriter writer = new FileWriter("c:\\存放取出数据的文件名.txt");
   while((len = reader.read(buffer))>0){
      writer.write(buffer,0,len);
   }
   //用流方式取出数据库中的二进制文件 //Blob,LongBlob
   InputStream in = rs.getBinaryStream("二进制文件存放列名"或列号);
   或
   InputStream in = rs.getBlob("二进制文件存放列名"或列号).getBinaryStream();
   byte buffer[] = new byte[1024];
   int len = 0;
   FileOutputStream out = new FileOutputStream("c:\\1.jpg");
   while((len=in.read(buffer))>0){
       out.write(buffer,0,len);
   }
   rs.getDate()          //DATE,获得的是java.sql.Date类型
   rs.getTime()          //TIME,获得的是java.sql.Time类型
   rs.getTimestamp()     //TIMESTAMP,获得的是java.sql.Timestamp类型
}

6.释放资源(如下代码要放在finally中)
rs.close();
st.close();
conn.close();java

 

2、数据分页显示(customer工程):mysql

MySql分页sql语句
select * from customer limit ?,?
Oracle分页sql语句
select * from(
    select rownum r_, row_.*  from(
select * from student order by id
    )row_ where rownum <=5  //5为起始索引位置
)where r_>=1  //1为结束索引位置

1.完成分页的dao支持
1.获取总纪录数的方法
2.根据起始位置,获得某一页数据的方法

二、写响应用户分页显示的servlet
1.调用dao得到总纪录数
2.获得用户查看哪一页
3.把上面两个信息,传递给page,并使用page对象负责完成计算逻辑
4.根据page计算出来的该页在数据库的起始位置,凋用dao得到页面数据
5.把封装了页面数据的list集合,封装到page对象中
6.把page对象,保存到request域中,交给jsp显示

三、jsp显示page对象数据
1.要显示page里面封装的list集合(即页面数据)
2.foreach循环显示page里面封装的开始页码和结束页码,以在jsp页面中显示页号

四、page对象完成什么计算逻辑?
1.要根据总纪录数,算出总页数
2.要根据用户所要查看页,算出该页的数据应该从数据库哪一个位置开始取
3.根据用户所要查看的页,计算jsp页面中,页号的起始码和结束码
4.page对象提供一个list集合,用于封装页面数据
修改的作法
1.回显用户的数据
1.点修改,访问servlet:FindCustomer,它负责取出须要修改的用户的数据
2.FindCustomer这个servlet把用户数据交给jsp回显
3.用户能够在jsp回显页面上修改用户数据,提交给EditCoustomer,EditCoustomer这个servlet负责用最新的用户数据,覆盖原有数据库的数据sql

 

3、JDBC调用存储过程步骤:数据库

1.获得CallableStatement,并调用存储过程
CallableStatement cs = conn.prepareCall("{call 方法(?, ?)}");

2.设置参数,注册返回值,获得输出
cs.registerOutParameter(2, Types.VARCHAR);
cs.setString(1, "abc");
cs.execute();
cs.getString(2);apache

 

4、手动写一个链接池数组

1.写一个自已的类实现java.sql.DataSource接口tomcat

 

5、经常使用数据库链接池(也叫数据源),实现了DataSoruce安全

1.DBCP 是 Apache 软件基金组织下的开源链接池实现
在工程中增长以下两个 jar 文件:
Commons-dbcp.jar:链接池的实现
Commons-pool.jar:链接池实现的依赖库
//dbcpconfig.properties为DBCP的配置文件
InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties prop = new Properties();
prop.load(in);
BasicDataSourceFactory factory = new BasicDataSourceFactory();
DataSource ds = factory.createDateSource(prop);  //获得数据源对象
ds.getConnection();  //返回Connection,供自已手写的dao调用
ds.getDataSource();  //DataSource,供DBUtils框架调用

2.C3P0
在工程中增长以下一个 jar 文件:
c3p0-0.9.1.2.jar:链接池的实现
ComBolDataSource ds = new ComboPooledDataSource();
ds.setDriverClass("com.mysql.jdbc.Driver");  //装载mysql数据库驱动
ds.setJdbcUrl("jdbc:mysql://localhost:3306/数据库名"); //注册
ds.setUser("数据库用户名");  
ds.setPassword("数据库密码");
ds.setMaxPoolSize(50);  //设置最大链接数
ds.setMinPoolSize(10);  //设置最小链接数
ds.setInitialPoolSize(20); //设置初始链接数
ds.getConnection();  //返回Connection,供自已手写的dao调用
ds.getDataSource();  //DataSource,供DBUtils框架调用

3.Tomcat的链接池
(1)、此种配置下,驱动jar文件需放置在tomcat的lib下
在WebRoot/META-INF/建军一个context.xml文件,写以下代码:
<Context>
  <Resource name="jdbc/任意名称a"  //为了让程序能够拿到这个配置文件
            auth="Container"
            type="javax.sql.DataSource"
            username="数据库用户名"
            password="数据库密码"
            driverClassName="com.mysql.jdbc.Driver"
            url="jdbc:mysql://localhost:3306/数据库名"
            maxActive="8"
            maxIdle="4"/>
</Context>

(2)、用JNDI(Java Naming and Directory Interface)技术拿到Tomcat的链接池:
在javax.naming包下,其核心API为Context,它表明JNDI容器,其lookup方法为检索容器中对应名称的对象。
Context initCtx = new InitialContext(); //jndi初始化
Context envCtx = (Context) initCtx.lookup("java:comp/env");//获得tomcat中的JNDI容器
DataSource ds = (DataSource) envCtx.lookup("jdbc/任意名称a"); //拿到了context.xml中配置的数据库链接池
ds.getConnection();  //返回Connection,供自已手写的dao调用
ds.getDataSource();  //DataSource,供DBUtils框架调用并发

 

6、事务oracle

1.事务的概念:
事务指逻辑上的一组操做,组成这组操做的各个单元,要不所有成功,要不所有不成功。
数据库开启事务命令
start transaction  开启事务
Rollback  回滚事务
Commit   提交事务
JDBC控制事务语句
//关闭JDBC默认提交方式,让多条SQL在一个事务中执行
Connection.setAutoCommit(false);  
//设置事务回滚点
Savepoint sp = conn.setSavepoint();
//回滚到上面设置的回滚点
Connection.rollback(sp);
//提交,回滚后必需要提交
Connection.commit();

2.事务的特性(ACID):
原子性(Atomicity):原子性是指事务是一个不可分割的工做单位,事务中的操做要么都发生,要么都不发生。?
一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态。
隔离性(Isolation):事务的隔离性是多个用户并发访问数据库时,数据库为每个用户开启的事务,不能被其余事务的操做数据所干扰,多个并发事务之间要相互隔离。
持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即便数据库发生故障也不该该对其有任何影响。

3.事务的隔离性:
脏读:指一个事务读取了另一个事务未提交的数据。
不可重复读:在一个事物内读取表中的某一行数据,屡次读取结果不一样。
和脏读的区别是:脏读是读取前一事务未提交的脏数据,不可重复读是从新读取了前一事务已提交的数据。
虚读:是指在一个事务内读取到了别的事务插入的数据,致使先后读取不一致。
数据库共定义了四种隔离级别:
Serializable:可避免脏读、不可重复读、虚读状况的发生。(串行化)    例:conn.TRANSACTION_SERIALIZABLE
Repeatable read:可避免脏读、不可重复读状况的发生。(可重复读)
Read committed:可避免脏读状况发生(读已提交)。
Read uncommitted:最低级别,以上状况均没法保证。(读未提交)
数据库存中对事务隔离性的操做语句:
set   transaction isolation level 设置事务隔离级别
select @@tx_isolation查询当前事务隔离级别

4.程序中设置隔离级别(conn为Connection类),此操做要在开启事务以前:
conn.setTransactionIsolation(conn.TRANSACTION_SERIALIZABLE);
conn.setTransactionIsolation(conn.TRANSACTION_REPEATABLE_READ);
conn.setTransactionIsolation(conn.TRANSACTION_READ_COMMITTED);
conn.setTransactionIsolation(conn.TRANSACTION_READ_UNCOMMITTED);

 

7、元数据-DataBaseMetaData

1.返回数据库的定义信息:
DataBaseMetaData db = Connection.getDatabaseMetaData():返回数据库元数据对象
db.getURL():返回一个String类对象,表明数据库的URL。
db.getUserName():返回链接当前数据库管理系统的用户名。
db.getDatabaseProductName():返回数据库的产品名称。
db.getDatabaseProductVersion():返回数据库的版本号。
db.getDriverName():返回驱动驱动程序的名称。
db.getDriverVersion():返回驱动程序的版本号。
db.isReadOnly():返回一个boolean值,指示数据库是否只容许读操做。

2.返回表的定义信息:
ParameterMetaData pd = PreparedStatement.getParameterMetaData():返回表明PreparedStatement对象的元数据
pd.getParameterCount():得到PreparedStatement对象传递的参数个数
pd.getParameterType(1):得到PreparedStatement对象传递的参数中第1个参数的sql类型

3.返回列的定义信息:
ResultSetMetaData rd = ResultSet.getMetaData():得到表明ResultSet对象的元数据对象
rd.getColumnCount():返回resultset对象的列数
rd.getColumnName(1):得到第1列的列名
rd.getColumnTypeName(1):得到第1列的列数据的类型 

 

8、DBUtils框架

共一个包:
org.apache.commons.dbutils
导jar包:commons-dbutils.jar
DbUtils类的方法(都是静态的):
DbUtils.close(conn,st,rs):关闭Connection、Statement和ResultSet。
DbUtils.closeQuietly(conn,st,rs):关闭Connection、Statement和ResultSet,隐藏异常。
DbUtils.commitAndCloseQuietly(conn): 用来提交链接,而后关闭链接,而且在关闭链接时不抛出SQL异常。 
DbUtils.loadDriver():装载并注册JDBC驱动程序,若是成功就返回true,不须要捕捉ClassNotFoundException异常。
QueryRunner类的主要方法:
runner.query(conn,sql,params,ResultSetHandler):查询操做,将结果集保存到指定JavaBean对象中
runner.query(sql,params,ResultSetHandler):查询操做,将结果集保存到指定JavaBean对象中
runner.query(conn,sql,ResultSetHandler): 查询操做,不用参数,将结果集保存到指定JavaBean对象中。
runner.update(conn,sql,params):用来执行一个更新(插入、更新或删除)操做。
runner.update(conn,sql):用来执行一个不须要置换参数的更新操做。
ResultSetHandler 接口的实现类:
ArrayHandler:把结果集中的第一行数据转成对象数组。
ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
ColumnListHandler:将结果集中某一列的数据存放到List中。
KeyedHandler(name):将结果集中的每一行数据都封装到一个小Map里,再把这些小map再存到一个另外一大map里,其map的key为传进来的key,如name。
MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
MapListHandler:将结果集中的每一行数据都封装到一个Map里,而后再存放到List

1.增删改操做(CUD):
QueryRunner runner = new QueryRunner(DataSource类);  //获得QueryRunner对象
String sql = "sql语句";  //这里的语句能够是CRUD语句
Object[] arr = {参数列表}; //构建参数对象数组
qr.update(sql,arr);   //将参数传给DBUtils框架,执行增删改操做

2.经常使用查找操做(R):
User user = runner.query(sql,params,new BeanHandler(user.class)); 
List list = runner.query(sql,params,new BeanListHandler(user.class));

经常使用O-R Mapping映射工具
Hibernate
Ibatis
Commons DbUtils(只是对JDBC简单封装)

 

9、CachedRowSet(离线操做数据库)

1.建立CachedRowSet
CachedRowSetImpl cache = new CachedRowSetImpl();

2.填充CachedRowSet
//对查出的结果集提供分页功能
cache.setPageSize(10);  //取10行数据
cache.populate(rs,2);  //从第2行开始取,起始值是1

3.更新CachedRowSet调用update***()方法后,调用updateRow方法更新至数据库,调用acceptChanges方法

相关文章
相关标签/搜索