Java(原生JDBC,连接池,DBUtils工具类)

目录

一.JDBC

二.原生JDBC开发的步骤

三.定义原生JDBCUtils.java工具类

四.DBCP连接池

五.C3P0连接池

六.DBUtils编写语句


一.JDBC

介绍:java语言操作各种数据库的技术
 *         JDBC由一堆接口和类组成
 *         类: DriverManger,用于注册驱动
 *         接口
 *             Connection:数据库连接类的根接口
 *             Statement:数据库语句执行类的根接口        
 *             ResultSet:结果集类的根接口
ps: 以上接口的实现类,在驱动包中。接口都在java.sql.xxx下。

二.原生JDBC开发的步骤

0.向工程中导入具体的数据库驱动包(jar包)
1.注册驱动
 *             DriverManger.register(new com.mysql.jdbc.Driver());//这样会注册两次
 *             Class.forName("com.mysql.jdbc.Driver");

ps:第一次会在类加载的时候会在静态代码块执行一次。
2.获取连接(四大要素:驱动,连接串,用户名,密码)
 *             前提:必须有驱动
 *             Connection conn = DriverManager.getConnection(
 *                     "jdbc:mysql://ip地址:3306/数据库名","用户名","密码"
 *                     );
3.获取sql语句执行对象
 *             Statement st = conn.createStatement();
 *             PreparedStatement pst = conn.prepareStatement(String sql);
 *             pst.setObject("占位符序号","具体的参数值");
ps:直接的conn.createStatement因为字符没转义如果不使用prepareStatement进行预处理,可能会受到sql注入攻击。
4.使用执行对象执行sql语句,获取到结果集
 *             ResultSet rs = st.excuteQuery(sql);
 *             ResultSet rs = pst.excuteQuery();
5.处理结果集
 *             rs.next();//判断有没有下一条记录
 *             rs.getObject("字段名");//rs.getObject(字段对应的序号);
6.释放资源
 *             rs.close(),st.close,conn.close()

三.定义原生JDBCUtils.java工具类

定义工具类:

  1. 首先获取配置文件,然后加载驱动。
  2. 创建数据库连接的静态成员方法。
  3. 创建关闭数据库资源的静态成员方法。
public class GJDBCUtils {
	//这个工具类,主要为我们获取一个数据库连接
		private static String driverName = null;
		private static String url = null;
		private static String username = null;
		private static String password = null;
		
		//静态代码块,目的,让第一次使用到JDBCUtils中加载驱动,第二次以后不再加载了
		static{
			//1.加载驱动
			try {
				//0.获取配置文件
				Properties ps = new Properties();
				ps.load(new FileInputStream("gjdbc_config.properties"));
				driverName = ps.getProperty("driverName");
				url = ps.getProperty("url");
				username = ps.getProperty("username");
				password = ps.getProperty("password");
				
				Class.forName(driverName);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				//System.out.println("驱动加载失败..请检查驱动包");
				throw new RuntimeException("驱动加载失败..请检查驱动包");
			}
		}
		
		public static Connection getConnection() throws Exception{
			//2.获取和数据库的连接
			Connection conn =  DriverManager.getConnection(url, username, password);
			//3.返回连接对象
			return conn;
			
		}
		//关闭所有资源的统一代码
		public static void closeAll(Connection conn,Statement st,ResultSet rs){
			//负责关闭
			if(conn != null){
				try {
					conn.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(st != null){
				try {
					st.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(rs != null){
				try {
					rs.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
}

进行查询操作:

  1. 使用静态方法进行连接。
  2. 创建statement对象,并执行查询语句。(一般为了避免sql注入,会预处理sql语句)
//查询
	public static void query(){
		//1.
		Connection conn = null;
		//2.
		Statement st = null;
		//3.
		ResultSet rs = null;
		//4.
		try{
			conn = JDBCUtils.getConnection();
			st = conn.createStatement();
			rs = st.executeQuery("select * from category where cid= 14");
			while(rs.next()){
				Object cid = rs.getObject("cid");
				Object cname = rs.getObject("cname");
				System.out.println(cid+"\t"+cname);
			}
			
		}catch(Exception e){
			System.out.println(e);
		}finally {
			JDBCUtils.closeAll(conn, st, rs);
		}
	}

预防sql注入的修改:

         String sql = "select * from users where username = ? and password = ?";
	st = conn.prepareStatement(sql);
			
	//设置值
	st.setObject(1,username);
	st.setObject(2, password);
			
	rs = st.executeQuery();

ps:防止SQL注入,内部会自动对sql语句进行转译,让这个sql语句中的变量和sql语法有关的字符都失效。

ps:PreparedStatement: 他是Statement的子类。

四.DBCP连接池

使用方式(推荐)
 *             不需要解析dbcpconfig.properties文件
 *             用到一个核心类: BasicDataSourceFactory
 *             DataSource ds = BasicDataSourceFactory.createDataSource(Properties对象);
 *             然后直接使用
 *             ds.getConnection();
补充:加载配置文件的两种方式:
 *       1.配置文件在工程的根目录
 *                Properties ps = new Properties();
 *                ps.load(new FileInputStream("配置文件名"));
 *        2.配置文件在src根目录
 *                Properties ps = new Properties();
 *                InputStream in = 当期类.class.getClassLoader().getResourceAsStream("配置文件名");
 *                ps.load(in);

public class DBCPUtils02 {
	
	private static DataSource ds = null;	
	//静态代码块,设置ds的四大要素
	static{
		try {
			Properties ps = new Properties();
//			ps.load(new FileInputStream("dbcpconfig.properties"));
			ps.load(DBCPUtils02.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"));
			ds = BasicDataSourceFactory.createDataSource(ps);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static Connection getConnection() throws SQLException{
		//返回一个连接对象,不要用DriverManager获取,而是连接池中获取
		return ds.getConnection();
	}
.......
}

五.C3P0连接池

使用方式:基于XML配置文件
     *         注意:
     *             1.文件名和文件中标签名 必须是官方指定的
     *             2.必须放到src根目录下,C3P0的实现类ComboPooledDataSource会自动加载
     *             使用
     *                 ComboPooledDataSource ds = new ComboPooledDataSource();
     *                 //不需要设置四大要素,因为ds会自动去加载 src下一个叫做次c3p0-config.xml的文件
     *                 ds.getConnection();

public class C3P0Utils02 {

	private static ComboPooledDataSource ds = new ComboPooledDataSource();
	
	public static Connection getConnection() throws SQLException{
		//获取连接,不要自己去DriverManager获取,而是从C3P0连接池获取
		return ds.getConnection();
	}
.......
}

六.DBUtils编写语句

DBUtils:主要用于 关闭连接,释放资源,和事务相关。
QueryRunner: 主要用于 增删改查 CURD
 *             int update(String sql,Object... params);//用于增删改
 *             不一定  query(String sql,ResultSetHandler接口实现类对象 ,Object...params);//用于查询的
ResultSetHandler

 *   (Object[])ArrayHandler:把结果集中的第一条记录,封装成Object[],返回这个Object数组
 *   (List)ArrayListHandler:把结果集中每一条记录,封装成Object数组,把这个数组再封装到集合中,并返回集合
 *   (JavaBean)BeanHandler:把结果集中的第一条记录,封装到JavaBean对象,并返回这个对象
 *    (List)BeanListHandler:把结果集中每一条记录,分别封装到JavaBean对象保存到集合中,返回这个集合   
 *    (List)ColumnListHandler:把结果集中的某一列的值,封装到list集合中,并返回
 *    (Map)MapHandler:把结果集中的第一条记录,封装到Map中。
 *    (List>)MapListHandler:把结果集中的每一条记录,封装到Map中。
 *    (Object)ScalarHandler:主要用来保存单一数据

//ArrayHandler处理类的使用
	public static void demo01() throws SQLException{
		//1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils02.getDataSource());
		//2.执行查询
		String sql = "select * from category";
		Object[] objs = qr.query(sql, new ArrayHandler());
		//3.打印
		for (Object obj : objs) {
			System.out.println(obj);
		}
	}
	
	//ArrayListHandler处理类的使用
	public static void demo02() throws SQLException{
		//1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils02.getDataSource());
		//2.执行查询
		String sql = "select * from category";
		List  list = qr.query(sql, new ArrayListHandler());
		//3.打印
		for (Object[] objects : list) {
			System.out.println(objects[0]+"\t"+objects[1]);
		}
	}
	//BeanHandler处理类的使用
	public static void demo03() throws SQLException{
		//1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils02.getDataSource());
		//2.执行查询
		String sql = "select * from category";
		Category c = qr.query(sql, new BeanHandler(Category.class));
		//3
		System.out.println(c);
	}
	//BeanListHandler处理类的使用
	public static void demo04() throws SQLException{
		//1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils02.getDataSource());
		//2.执行查询
		String sql = "select * from category";
		List list = qr.query(sql, new BeanListHandler(Category.class));
		//3
		for (Category category : list) {
			System.out.println(category);
		}
	}
	
	//ColumnListHandler处理类的使用
	public static void demo05() throws SQLException{
		//1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils02.getDataSource());
		//2.执行查询
		String sql = "select * from category";
		List ids = qr.query(sql, new ColumnListHandler("cname"));
		//3
		System.out.println(ids);
	}
	
	//MapHandler处理类的使用
	public static void demo06() throws SQLException{
		//1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils02.getDataSource());
		//2.执行查询
		String sql = "select * from category";
		Map map = qr.query(sql,new MapHandler());
		//3
		System.out.println(map);
	}
	//MapListHandler处理类的使用
	public static void demo07() throws SQLException{
		//1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils02.getDataSource());
		//2.执行查询
		String sql = "select * from category";
		List> maps = qr.query(sql,new MapListHandler());
		//3
		System.out.println(maps);
	}
	
	//ScalarHandler处理类的使用
	public static void demo08() throws SQLException{
		//1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils02.getDataSource());
		//2.执行查询
		String sql = "select count(*) from category";
		Object count = qr.query(sql,new ScalarHandler());
		//3
		System.out.println(count);
	} 
   
  

七.DBUtils事务

数据库分层逻辑:

\utils\C3P0Utils02 .java   ...  ConnectionManager.java

连接池获取连接  并且 定义连接池的逻辑

\dao\AccountDao.java

定义数据库查询逻辑

\service\AccountService.java

定义业务服务逻辑

\view\AccountView.java

定义web请求的服务

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

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

\utils\C3P0Utils02 .java

连接池获取连接

\utils\ConnectionManager.java

负责获取连接,开启事务,提交事务,回滚事务,关闭连接

定义连接池的逻辑

原理:存放的value是当前线程中是要共享的数据。

作用:使用ThreadLocal来管理连接,保证同一个线程为同一个连接。

ps:如果不为同一个连接(相当于不同的用户)操作,会出bug的,必须保证同一个连接。

public class ConnectionManager {
	//1.定义一个集合 ThreadLocal 对象来保存当前线程的连接
	private static ThreadLocal tl = new ThreadLocal();
	//2.获取连接
	public static Connection getConnection() throws SQLException{
		//1.先从tl中获取连接
		Connection conn =  tl.get();
		//2.判断conn是否为空
		if(conn == null){
			//说明 是service层第一次获取
			conn = C3P0Utils02.getConnection();
			tl.set(conn);
		}
		//如果不为空 说明 是dao层第二次以后获取
		return conn;
	}
	//3.开启事务
	public static  void start() throws SQLException{
		ConnectionManager.getConnection().setAutoCommit(false);
	}
	//4.提交事务
	public static void commit() throws SQLException{
		ConnectionManager.getConnection().commit();
	}
	//5.回滚事务
	public static void rollback() throws SQLException{
		ConnectionManager.getConnection().rollback();
	}
	//6.关闭连接
	public static void close() throws SQLException{
		ConnectionManager.getConnection().close();
	}
}

\dao\AccountDao.java

定义数据库查询逻辑

public class AccountDao {
	//转账出去
	//jack 转出去 1000元
	public void fromAccount(String fromName,double money) throws SQLException{
		Connection conn = ConnectionManager.getConnection();
		//1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner();
		//2.执行减钱操作
		qr.update(conn,"update account set money=money-? where name=?", money,fromName);
	}
	//收钱回来
	public void toAccount(String toName,double money) throws SQLException{
		Connection conn = ConnectionManager.getConnection();
		//1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner();
		//2.加钱操作
		qr.update(conn,"update account set money=money+? where name=?",money,toName);
	}
}

\service\AccountService.java

定义业务服务逻辑

public class AccountService {
	//转账业务
	public void transfer(String fromName,String toName,double money){
		//直接调用dao
		AccountDao dao = new AccountDao();
		try {
			//开启事务
			ConnectionManager.start();
			//转出去
			dao.fromAccount(fromName, money);
			//模拟异常
			System.out.println(1/0);
			//拿回来
			dao.toAccount(toName, money);
			//提交事务
			ConnectionManager.commit();
			System.out.println("转账成功!!!");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			System.out.println("程序出现异常,程序回滚");
			//回滚事务
			try {
				ConnectionManager.rollback();
			} catch (SQLException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}finally {
			try {
				ConnectionManager.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}	
}

\view\AccountView.java

定义web请求的服务

public class AccountView {
	public static void main(String[] args) {
		//模拟用户转账,输入付款人的姓名,收款人的姓名,转账金额
		String fromName = "jack";
		String toName = "tom";
		double money = 5000;
		AccountService service = new AccountService();
		service.transfer(fromName, toName, money);
	}
}

事务特性

  • 原子性:强调事务的不可分割.多条语句要么都成功,要么都失败。
  • 一致性:强调的是事务的执行的前后,数据要保持一致.
  • 隔离性:一个事务的执行不应该受到其他事务的干扰.
  • 持久性:事务一旦结束(提交/回滚)数据就持久保持到了数据库.

如果不考虑事务的隔离性,会引发一些安全性问题

* 脏读      :一个事务读到另一个事务还没有提交的数据.

* 不可重复读 :一个事务读到了另一个事务已经提交的update或者delete的数据,导致在当前的事务中多次查询结果不一致.

* 虚读/幻读  :一个事务读到另一个事务已经提交的insert的数据,导致在当前的事务中多次的查询结果不一致.

解决引发的读问题:(设置事务的隔离级别)

* 1 read uncommitted       :未提交读.脏读,不可重复读,虚读都可能发生.

* 2 read committed    :已提交读.避免脏读.但是不可重复读和虚读有可能发生.(Oracle默认)

* 4 repeatable read     :可重复读.避免脏读,不可重复读.但是虚读有可能发生.(MySql默认)

* 8 serializable           :串行化的.避免脏读,不可重复读,虚读的发生.

总结

       事务的并发安全需要隔离级别的控制。

       连接一致确保了单事务的安全。

你可能感兴趣的:(数据库,java)