Servlet Java Web开发(5)数据库的使用
1经典使用
核心API及其使用,maven:mysql-connector-java
连接数据库
String db_url="jdbc:mysql://localhost:3306/djangoserverTimezone=GMT%2B8";
Connection con=DriverManager.getConnection(db_url,user,pwd);
操作
Statement stmt=con.createStatement();
对于增加,修改,删除使用stmt.executeUpdate,返回作用的行数
对于查询使用stmt.executeQuery,返回ResultSet对象,比如叫rs。获取数据方式如下
while(rs.next()){
...String id=rs.getInt("id");..}
例子:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; public class MainFIle { /** * @param args * @throws ClassNotFoundException */ public static void main(String[] args) throws ClassNotFoundException { // TODO Auto-generated method stub String jdbc_driver="com.mysql.cj.jdbc.Driver"; String db_url="jdbc:mysql://localhost:3306/django?serverTimezone=GMT%2B8"; String user="root"; String pwd="123456"; Connection con=null; java.sql.Statement stmt=null; try { con=DriverManager.getConnection(db_url,user,pwd); stmt=con.createStatement(); String sql="select * from personinfo"; ResultSet rs=stmt.executeQuery(sql); while(rs.next()) { int id=rs.getInt("id"); String name=rs.getString("name"); String job=rs.getString("job"); System.out.println("well,here is your data: id is "+id+"with name: "+name+" and job:"+job); } rs.close(); stmt.close(); con.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { try { if(stmt!=null) stmt.close(); }catch(Exception ex) { ex.printStackTrace(); } try { if(con!=null) con.close();} catch(Exception ex) { ex.printStackTrace(); } } } }
PreparedStatement
是Statement的子接口,可以提高效率和防止SQL攻击。
使用:使用sql语句模板,用户数据字符串用?代替
String sql="insert into user value(?,?,?)";
String update="update user set username=?,password=? where id=?";
声明变量,和设置数据,序号从1开始
PreparedStatement ps=con.prepareStatement(update);
ps.setString(1,"haha");ps.setString(2,"234");
ps.executeUpdate()或者其他
需要执行其他数据,先清理,ps.clearParameters();
批处理:
在一次设置好所有数据之后,可以暂不执行,调用ps.addBatch(),再继续设置数据
最后一次性ps.executeBatch()
存取二进制数据
二进制在表中的数据类型为BLOB,在代码中操作的数据类型为InputStream
建表时,该列字段为MDDIUMBLOB (16MB)或者LONGBLOB(4GB),字段名为mydata
将数据读入一个InputStream in中,使用PreparedStatement构建sql模板比如
String sql="insert into yourtable (filename,mydata) values(?,?)";
使用ps .setBinaryStream(2,in);
读取时,使用ResultSet的getBinaryStream("mydata")方法获取InputStream对象
方法2:存取的时候使用PreparedStatement的setBlob和
ResultSet的getBlob方法。
Blob 对象和字节数组可以相互转换。Blob b=new SerialBlob(..)参数是byte[]
getBlob返回一个Blob对象b后,使用getBytes返回byte array
DAO模式
就是写一个类,封装访问数据的代码,在数据库和业务逻辑之间。
1.实体域,比如我们操作的是User表,先写一个User类
public class User{
private String id;
private String userName;...}
2.DAO模式需要一个DAO接口
public interface UserDao{
public void add(User user);
public void mod(User user);
public void del(String id);....}
3该接口的一个实现,实现具体访问数据库的细节。
JdbcUtils辅助类
目的:将mysql路径,用户名密码放到配置文件中。
2.事务和连接池
事务开始,
执行多条sql语句
要么提交要么回滚
1开启事务
setAutoCommit(false),默认为true,也就是每一条sql语句都是一个单独的事务
2commit or rollback
try {
con.setAutoCommit(false);//开启事务…
….
…
con.commit();//try的最后提交事务
} catch() {
con.rollback();//回滚事务
}
连接池库C3P0
在src文件夹建立c3p0-config.xml文件,内容
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <!-- 这是默认配置信息 --> <default-config> <!-- 连接四大参数配置 --> <property name="jdbcUrl">jdbc:mysql://localhost:3306/customers</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="user">root</property> <property name="password">123</property> <!-- 池参数配置 --> <property name="acquireIncrement">3</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">2</property> <property name="maxPoolSize">10</property> </default-config> <!-- 专门为oracle提供的配置信息 --> <named-config name="oracle-config"> <property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb1</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="user">root</property> <property name="password">123</property> <property name="acquireIncrement">3</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">2</property> <property name="maxPoolSize">10</property> </named-config> </c3p0-config>
使用,需要的时候直接从池中getConnection,用完关闭。
ComboPooledDataSource ds = new ComboPooledDataSource(); Connection con = ds.getConnection(); ........... con.close();
基于ThreadLocal的Jdbc实用类
该类保证了一个变量,在不同的线程有一个唯一对象
ThreadLocal<T>简介,内部是个map,使用当前线程作为键。
ThreadLocal<Connection> con=new ThreadLocal<Connection>();
con保证了每一个线程con都有一个Connection对象。主要三个方法
void set(T value);
T get();
void remove();
该实用类的全部代码
package cn.itcast.jdbc; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; /** * 使用本类的方法,必须提供c3p0-copnfig.xml文件 * @author qdmmy6 */ public class JdbcUtils { // 饿汉式 private static DataSource ds = new ComboPooledDataSource(); /** * 它为null表示没有事务 * 它不为null表示有事务 * 当开启事务时,需要给它赋值 * 当结束事务时,需要给它赋值为null * 并且在开启事务时,让dao的多个方法共享这个Connection */ private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); public static DataSource getDataSource() { return ds; } /** * dao使用本方法来获取连接 * @return * @throws SQLException */ public static Connection getConnection() throws SQLException { /* * 如果有事务,返回当前事务的con * 如果没有事务,通过连接池返回新的con */ Connection con = tl.get();//获取当前线程的事务连接 if(con != null) return con; return ds.getConnection(); } /** * 开启事务 * @throws SQLException */ public static void beginTransaction() throws SQLException { Connection con = tl.get();//获取当前线程的事务连接 if(con != null) throw new SQLException("已经开启了事务,不能重复开启!"); con = ds.getConnection();//给con赋值,表示开启了事务 con.setAutoCommit(false);//设置为手动提交 tl.set(con);//把当前事务连接放到tl中 } /** * 提交事务 * @throws SQLException */ public static void commitTransaction() throws SQLException { Connection con = tl.get();//获取当前线程的事务连接 if(con == null) throw new SQLException("没有事务不能提交!"); con.commit();//提交事务 con.close();//关闭连接 con = null;//表示事务结束! tl.remove(); } /** * 回滚事务 * @throws SQLException */ public static void rollbackTransaction() throws SQLException { Connection con = tl.get();//获取当前线程的事务连接 if(con == null) throw new SQLException("没有事务不能回滚!"); con.rollback(); con.close(); con = null; tl.remove(); } /** * 释放Connection * @param con * @throws SQLException */ public static void releaseConnection(Connection connection) throws SQLException { Connection con = tl.get();//获取当前线程的事务连接 if(connection != con) {//如果参数连接,与当前事务连接不同,说明这个连接不是当前事务,可以关闭! if(connection != null &&!connection.isClosed()) {//如果参数连接没有关闭,关闭之! connection.close(); } } } }
DBUtils类
C3P0是连接池类,主要目的是获得Connection对象,
而DButils类需要一个Connection对象,对ResultSet中的数据操作进行了转换和简化。
你现在可以把ResultSet中数据放到一个List中,或者Map中,或者一个bean中
DBUtils主要是QueryRunner类
以update为例子
update函数原型
public int update(Connection con ,String sql);
public int update(Connection con,String sql,Object...params);//为了传入PreparedStatement 数量不一的?形式的参数
String sql="insert into user values(?,?,?)";
QueryRunner qr=new QueryRunner(con);
qr.update(sql,"23","sam","123");
con.close();
以query为例子
在DAO模式下,有类Customer属性分别是String id,String name,String date
数据库表t_customer分别对应。
public <T> T query(Connection,String sql,ResultSetHandler<T> rsh)或者
public <T> T query(Connection,String sql,ResultSetHandler<T> rsh,Object...params)
接口ResultSetHandler有很多不同形式的实现,方便 返回不同的数据类型。
例1.返回所有
String sql="select * from t_customer";
返回Customer对象的列表。BeanListHandler<T>实现的是ResultSetHandler<List<T>>
构造函数为public BeanListHandler(Class<T> type),参数为bean的Class类型,这里传入Customer.class
public List<Customer> findAll(){
QueryRunner qr=new QueryRunner(con);
String sql="select * from t_customer";
return qr.query(sql,newBeanListHandler<Customer>(Customer,class));
}
例2,返回id为XX的一个对象
public Customer load(String cid){
QueryRunner qr=new QueryRunner(con);
String sql="select * from t_customer where cid=?";
return qr.query(sql,new BeanHandler<Customer>(Customer.class));
}
其他实现举例
MapHandler 将结果转成Map<String,Object>其中键就是数据表的列
MapListHandler结果转成List<Map<String,Object>,
ColumnListHandler转成List<Object>,需要指定列的名字,new ColumnListHandler("name");
ScalarHandler用于集聚查询,select count(*) from tableXXX;
在代码中的使用:除了第三个,直接传入一个new的对象。