一、jdbc基础
1.连接参数关闭顺序:倒关
/**
* 关闭资源
* 倒关
*/
rs.close();
statement.close();
conn.close(); //这个必须关
2.jdbc连接时的规范化处理
@Test
public void fun1() throws SQLException {
//先初始化
Connection conn=null;
Statement statement=null;
ResultSet rs=null;
try{
String className="com.mysql.cj.jdbc.Driver";
String uri="jdbc:mysql://localhost:3306/javaweb?serverTimezone=GMT%2B8&useSSL=true";
String username="root";
String password="123456";
Class.forName(className);
conn= DriverManager.getConnection(uri,username,password);
statement=conn.createStatement();
String sql="select * from stu";
rs=statement.executeQuery(sql);
ResultSetMetaData metaData=rs.getMetaData();
int count=metaData.getColumnCount();
while(rs.next()){
for(int i=1;i<=count;i++){
System.out.print(rs.getString(i));
if(i<count)
System.out.print(",");
}
System.out.println();
}
}catch (Exception e){
throw new RuntimeException(e);
}finally {
//一定执行,若不为null,则需要关闭
if(rs!=null)
rs.close();
if(statement!=null)
statement.close();
if(conn!=null)
conn.close();
}
}
3.获取结果集元数据
得到元数据:rs.getMetaData(),返回值为ResultSetMetaData
获取结果集列数:int getColumnCount()
获取指定列的列名:String getColumnName(int colindex)
4.PreparedStatement:Statement的子接口
防SQL攻击,提高代码的可读性,可维护性,提高效率
1.给出sql模板,所有的参数用?替代
2.调用Connnection方法,得到PrepareStatement
3.调用setString\setInt\....给参数赋值
4.调用查询方法,向数据库发送查询语句,executeQuery
5.sql.Date和Util.Date类型的转换
领域对象(domain)中的所有属性不能出现java.sql包下的东西,既不能使用java.sql.Date
ResultSet->getDate中的Date是java.sql.Date()
PreparedStatemetn->setDate(int,Date)中的Date是java.sql.Date()
时间类型的转换:
java.util.Date->java.sql.Date\Time\TimeStamp
将util转换成毫秒值
再将毫秒值创建sql的Date
java.sql.Date\Time\TimeStamp->java.util.Date
这一步不需要处理,因为java.sql.Date是java.util.Date的子类
6.jdbc批处理
for(int i=0;i<1000;i++){
statement.setString(1,i+1+"");
statement.setString(2,i+2+"");
statement.addBatch();
}
long start=System.currentTimeMillis();
statement.executeBatch();
long end=System.currentTimeMillis();
System.out.println(end-start);
注意:进行批处理,需要在url中设置rewriteBatchedStatements=true
二、事务
1.事务的四大特性:
原子性:不可再分割,要么全成功,要么全失败
一致性:事务执行后,数据库状态与其他业务规则保持一致。如转账业务,无论事务执行成功与否,参与转账两个账户金额总额是不变的
隔离性:隔离性是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰
持久性:事务一旦成功,事务中的所有数据必须被持久化到数据库中,即使提交事务后,数据库马上奔溃,在数据库重启时,也必须能保证通过某种方式恢复数据
2. sql语言开始事务:start transaction
结束事务:commit 和 rollback
3.在jdbc处理事务,都是通过Connection完成的!
同一事务中的所有操作,都在使用同一Connection对象!!!!
Connection三个方法与事务相关:
setAutoCommit(boolean)
conn.setAutoCommit(false):表示开启事务
commit():提交结束事务
rollback():回滚结束事务
4. jdbc处理事务的代码格式:
try{
conn.setAutoCommit(false);//开启事务
......
......
conn.commit();//执行事务
}catch(){
conn.rollback();//回滚事务
}
注意:使用同一Connection对象!!!!
5.事务的并发读问题:
脏读:读取到另一个事务未提交数据
三、数据库连接池
连接池也是使用四大连接参数来完成创建连接对象
1.连接池必须实现:javax.sql.DataSource接口
连接池返回的Connection对象,它的close方法与众不同,调用它的close不是关闭,而是把连接归还给池
2. 创建连接池对象---> 配置四大参数---> 配置池参数 ---> 得到连接对象
3. 连接池内部使用了四大参数创建了对象,即mysql驱动提供的Connection
连接池使用了mysql的连接对象进行了装饰,只对close方法进行了增强
装饰之后的Connection的close方法,用来把当前连接归还给池
附:装饰者模式(见OneNote笔记)
4.C3P0配置文件(开源免费的连接池)
(1)导入:c3p0-jar 和 mchange-commons-java
(2)配置文件要求:
文件名称:必须叫c3p0-config.xml
文件位置:必须在src下
<?xml version="1.0" encoding="UTF-8" ?>
<c3p0-config>
<default-config>
<!--四大配置参数-->
<property name="jdbcUrl"><![CDATA[jdbc:mysql://localhost:3306/javaweb?serverTimezone=GMT%2B8&useSSL=true]]></property>
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="user">root</property>
<property name="password">123456</property>
<!--池参数配置-->
<property name="initialPoolSize">10</property>
<property name="acquireIncrement">3</property>
<property name="maxPoolSize">10</property>
<property name="minPoolSize">2</property>
</default-config>
</c3p0-config>
(3)JdbcUtil.java如下(装饰者模式):
package C3P0;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class JdbcUtils {
//配置文件的默认配置,要求你必须给出c3p0-config.xml!!!!
private static ComboPooledDataSource dataSource=new ComboPooledDataSource();
/**
* 使用连接池返回一个连接对象
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
/**
* 返回连接池
* @return
*/
public static DataSource getDataSource(){
return dataSource;
}
5.Commons DbUtils 1.7 jar包
导入 Commons DbUtils 1.7 jar包,简易操作数据库
其中方法的实现是依靠反射机制实现的
增删改操作:
1.创建QueryRunner,需要提供数据库连接池对象(即可使用以上c3p0其配置)
QueryRunner qr=new QueryRunner(JdbcUtils.getDataSource());
2.给出sql
String sql="insert into user values(?,?)";
3.给出参数数组
Object [] params={"大佬","666666"};
4.传入sql和参数数组,执行增删改操作
qr.update(sql,params);
查询操作:
1.创建QueryRunner,需要提供数据库连接池对象
QueryRunner qr=new QueryRunner(JdbcUtils.getDataSource());
2.给出sql
String sql="select * from user where username=?";
3.执行query()方法,需要给出结果集处理器,即ResultSetHandler的实现类对象
我们要给出的是BeanHandler,它实现了ResultSetHandler
它需要一个类型,它会把rs中数据封装到指定类型的javabean对象中,然后返回到javabean中
注意:此对象需要数据库的属性的对象的属性及名称完全一致
User user=qr.query(sql,new BeanHandler<User>(User.class),"大佬");
System.out.println(user);
附:dbUtils其他方法,见OneNote
6.tomcat配置连接池
jndi作用:
tomcat配置jndi资源
1.在tomcat的conf文件夹下的context.xml配置文件中加入:
<Resource name="jndi/learn"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/javaweb?serverTimezone=GMT%2B8&useSSL=true"
username="root"
password="123456"
/>
2.在项目的web.xml中加入资源引用:
<resource-ref>
<description>JNDI DataSource</description>
<res-ref-name>jndi/learn</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
注意:其中res-ref-name值要和context.xml的name值一致。
7.ThreadLocal
ThreadLocal通常用在一个类的成员上
多个线程访问它时,每个线程都有它的副本,互不干扰!
Spring中把Connection放到了ThreadLoacal中!(后期继续改进JdbcUtils)
@Test
public void fun1(){
ThreadLocal<String> threadLocal=new ThreadLocal<String>();
threadLocal.set("hello");
new Thread(){
public void run(){
System.out.println(threadLocal.get());
}
}.start();
threadLocal.remove();
}
其输出结果为:null,即利用ThreadLocal不同线程资源互不干扰。