Javaweb - JDBC

 

目录

java中与数据库进行交互的技术

用statement插入数据库

集合、反射、泛型复习

集合(对同一组数据进行统一管理):

泛型(不确定的类型,从定义类结构的角度来理解)

反射

BaseDao类的设计

JDBC元数据类型

JDBC处理BLOB类型的文件数据

JDBC数据库连接池

DBUtils工具类的使用

JDBC调用存储过程和函数


java中与数据库进行交互的技术

1.JDBC直接访问数据库,是Java与各种数据库之间进行操作的公共API,能使程序连接数据库,执行SQL语句,并获取到数据库返回的结果。

2.JDO技术也是一个用于存取数据库中对象的标准API

3.第三方框架工具,如Hibernate,Mybatis等,只是对JDBC进行了二次封装

 

Driver接口:是所有JDBC驱动程序都需要实现的接口

连接数据库:

        //连接数据库,用到Driver接口的实现类(MySQL数据库开发者写的)
		Driver driver = new Driver();
	
		String url = "jdbc:mysql://localhost:3306/jdbc_1";//想要连接的数据库地址,样式为:  “jdbc:mysql://<host>:<port>/database_name”
		Properties info = new Properties();
		info.put("user", "root");//属性类型的参数,必须包含两个键值对,用户名与密码
		info.put("password", "root");
		//连接数据库
		Connection conn = driver.connect(url, info);

使用DriverManager:1.代码更简洁  2.可同时管理多个驱动程序

package cn.hanlin.jdbc;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.sql.SQLException;
import java.util.*;

//第一次用的单例模式,但发现静态方法其实更适用
public class DBUtils {
//	//把工具类变量单列模式
//	private DBUtils() {}
//	private static DBUtils dbu = null;
//	public static DBUtils getInstance() {
//		if(dbu == null) {
//			dbu = new DBUtils();
//		}
//		return dbu;
//	}
	
	public static Connection getConnection() throws SQLException, InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {
        //把配置文件中的值取出来
		Properties pop = new Properties();
		InputStream in = DBUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
		pop.load(in);
		String driverClass = pop.getProperty("driverClass");
		String url = pop.getProperty("url");
		String user = pop.getProperty("user");
		String password = pop.getProperty("password");
		
		
		//第一步加载数据库驱动,给DriverManager注册驱动
		Class.forName(driverClass);
		//可同时注册进去多个驱动
		//通过DriverManager的getconnection方法直接就获取到了数据库的连接
		Connection conn = DriverManager.getConnection(url, user, password);
		return conn;
	}
}


//jdbc.properties文件

driverClass = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/jdbc_1
user = root
password = root

总结:

1.准备好JDBC连接数据库的4个常量字符串(driver,url,user,password)。

2.新建配置文件放在类目录下,将上面的字符串以键值对的形式放入。

3.创建Properties对象,并将配置文件载入其中并读出来。

4.加载数据库驱动

5.通过DriverManager的getConnection()方法拿到数据库的连接

 

用statement插入数据库

     
      Connection conn = null;
	  Statement stat = null;
	  int count = 0;
	  try{
		  //获取数据库连接
		  conn = DBUtils.getConnection();
		  String sql = "INSERT INTO t_user(`username`,`password`) VALUES('"+username+"','"+password+"');";
		  //获取操作SQL语句的statement对象
		  stat = conn.createStatement();
		  count = stat.executeUpdate(sql);//执行SQL语句,只能是insert、delete、update语句,不能是select,返回结果
		  
	  }catch(Exception e){
		  e.printStackTrace();
	  }finally{
		  try{    //注:关闭顺序,先开启的后关闭
			//关闭statement对象
			 if(stat != null) stat.close();
		  }catch(Exception e){
			  e.printStackTrace();
		  }finally{
			  try{
					//关闭数据库连接
					if(conn != null)  conn.close();
				  }catch(Exception e){
						  e.printStackTrace();
					  }
			}
		}

ResultSet接口查询一条数据:

Connection conn = DBUtils.getConnection();
String sql = "select id,username,password from t_user where id=6;";
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery(sql);//执行查询语句,获得结果集
//ResultSet有next方法,使光标移到下一行(现在光标在标题行,只能下移),若有值返回true
while(rs.next()){
     int id = getInt("id");//取得数据,getObject方法
        ...
}

面向对象的的编程思路:将表里的数据都封装成相应的类,传递信息时,以类的对象作为基本的传递单元,一切皆对象!

 

PrepareStatement:

String sql = "INSERT INTO t_user(`username`,`password`) VALUES(?,?)";
//获取数据库连接
conn = DBUtils.getConnection();

//获取操作SQL语句的statement对象
stat = conn.prepareStatement(sql);

stat.setString(1, u.getUserName());
stat.setString(2, u.getPassword());

count = stat.executeUpdate();//执行SQL语句,返回结果

 

集合、反射、泛型复习

集合(对同一组数据进行统一管理):

集合中分成三大接口:Collection、Iterator、Map 都在java.util中

 

ArrayList是有序的,可以放很多不同类型的数据,但这是不好管理的,常用泛型进行限制:

List<Integer> list = new ArrayList<>();

list.add()、list.get(index)、list.size()、list.remove(index)、、、

 

Map中键是不能重复的,且键和值是一一对应的

HashMap中键和值都是可以为空的,但键只能有一个为空,没有顺序

map.put(k,v)、map.get(key)、map.containsKey(k)、map.containsValue(v)

遍历:先变成Set集合 用Entry接口、entrySet()方法

for(Entry<String, Integer> entry : map.entrySet()) {
			System.out.println(entry.getKey()+"-------"+entry.getValue());
		}

泛型(不确定的类型,从定义类结构的角度来理解)

数组不支持泛型

定义类:

public class Person<T> {
	private String name;
	private T age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public T getAge() {
		return age;
	}
	public void setAge(T age) {
		this.age = age;
	}
	public Person() {}
	public Person(String name, T age) {
		super();
		this.name = name;
		this.age = age;
	}
public class FxTest {

	public static void main(String[] args) {
		Person<Integer> p = new Person<>();
		p.setAge(12);
		
		Person<String> p1 = new Person<>();
		p1.setAge("12");
		
		Person p2 = new Person();//泛型擦除,为object类
		p2.setAge(12);
		print(p);
		print(p1);
		
	}
	
	public static void print(Person<?> person) {	//泛型通配符,只能接受输出,不能修改,因为不知道是什么类型
		person.getAge();
	}

定义方法:

public <T> T say() {		//不确定返回值
			
			return null;		//返回泛型类型
		}	

泛型上下限:

<? extends T>代表是T的某个子类,<? super T>代表是T的某个父类,均包含T类

 

反射

什么都不知道--->获取对象--->知道类的信息并且能修改类属性还能调用类方法

Class,一切类的反射根源

得到Class类的对象有三种方式:

1.Object类中的getClass()方法  2.类.class  3.通过Class类的forName()方法

User user = new User();
Class c = user.getClass();
Class c2 = User.class;
Class c3 = Class.forName("cn.xyz.model.User");    //为包全名

Class的一些常用方法:

1.创建对象:newInstance()

User user2 = (User) c.newInstance();	//返回类型为Object

2.获取和使用构造方法:getConstructors和getConstructor

                Constructor ct = c.getConstructor(int.class,String.class,String.class);
		Constructor ct1 = c.getConstructor();
		Constructor[] carray = c.getConstructors();
		for(int i=0;i<carray.length;i++) {
			System.out.println(carray[i].getName());
		}
		//获得public方法,getDeclaredMethod/getDeclaredMethods(获取到所有方法),包括private,				前提:method.setAccessible(true)忽略修饰符的限制,通过method.invoke(cat,方法参数)来调用方法
		Method method = c.getMethod("getId");	
		//获取方法名
		System.out.println(method.getName());
		
		//获得public字段,对应的getDeclaredField/getDeclaredFields,field.get(cat)获取属性值,field.set(cat)设置属性值
		Field[] field = c.getFields();	
		Field field2 = c.getDeclaredField("userName");
		field2.setAccessible(true);
		field2.set(user, "小白");		//很暴力,略过private限制,也跳过setter方法,强行给属性赋值
		System.out.println(field2.get(user));

ResultSetMetaData这个类就是jdbc的元数据类,从这个类的对象里就可以拿到结果集里有多少列,列名是什么...等等信息

编写通用的查询方法:

public static <T> T getOneData(Class clazz,String sql,Object... args){
		Connection conn = null;
		PreparedStatement stat = null;
		ResultSet rs = null;
		T entity = null;
		try {
			//连接数据库
			conn = DBUtils.getConnection();
			//获取PrepareStatement对象stat
			stat = conn.prepareStatement(sql);
			//替换掉SQL语句中的占位符?
			for(int i=0;i<args.length;i++) {
				stat.setObject(i+1, args[i]);
			}
			rs = stat.executeQuery();
			//从结果集中取出数据值
			if(rs.next()) {
				entity = (T) clazz.newInstance();//通过反射拿到这种类型的对象
				//首先不知道entity里有哪些属性
				ResultSetMetaData rsmd = rs.getMetaData();
				int columnCount = rsmd.getColumnCount();
				Map<String, Object> map = new HashMap<>();
				//把结果集中的数据取出来,放入一个map
				for(int i=1;i<=columnCount;i++) {
					String key = rsmd.getColumnLabel(i);
					Object val =  rs.getObject(key);
					map.put(key,val);
				}
				//把取出来的值封装到entity中
				for(Entry<String, Object> entry:map.entrySet()) {
					Field field = clazz.getDeclaredField(entry.getKey());
					field.setAccessible(true);
					field.set(entity, entry.getValue());
				}
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			DBUtils.close(conn, stat, rs);
		}
		return entity;
	}

 

BaseDao类的设计

专门用来访问和操作数据库的模块,主要就是对数据表中的记录做增删改查操作,是模块化编程思想的产物

可以直接使用,也可以被继承

大概应该有的方法:

1.实现数据表的增改删操作

2.查询一条记录

3.查询多条记录

4.查询条件某个字段的值或某个统计字段

BeanUtils工具类的使用:主要作用是操作类里的属性

		User user = new User();
		
		System.out.println(user);
		
		BeanUtils.setProperty(user, "userName", "test");
		
		System.out.println(BeanUtils.getProperty(user, "userName"));

 

JDBC元数据类型

DatabaseMetaData常用方法(了解内容)

			Connection connection = DBUtils.getConnection();
			//记录集元数据,rs.getMetaData();
			DatabaseMetaData dmd = connection.getMetaData();
			System.out.println(dmd.getURL());
			System.out.println(dmd.getUserName());
			System.out.println(dmd.isReadOnly());
			System.out.println(dmd.getDatabaseProductName());
			System.out.println(dmd.getDriverName());
			System.out.println(dmd.getDatabaseProductVersion());
			System.out.println(dmd.getDriverVersion());

			String sql = "SELECT id id,`username` userName,`password` "
					+"password FROM `t_user` WHERE id=?;";
			PreparedStatement stat = connection.prepareStatement(sql);
			stat.setInt(1, 13);
			ResultSet rs = stat.executeQuery();
			ResultSetMetaData rsmd = rs.getMetaData();
			System.out.println(rsmd.getColumnName(1));
			System.out.println(rsmd.getColumnCount());
			System.out.println(rsmd.getColumnLabel(2));
			System.out.println(rsmd.getColumnTypeName(1));
			System.out.println(rsmd.getColumnDisplaySize(1));
			System.out.println(rsmd.isNullable(1));
		    System.out.println(rsmd.isAutoIncrement(1));

 

JDBC插入新值时获得新记录的主键值

 

JDBC处理BLOB类型的文件数据

注意:如果存储的文件过大,数据库性能会下降

	String sql = "SELECT `pic_head` FROM `t_user` WHERE id=?;";
	Blob blob = baseDao.getOneColumnBlob(sql, 15);
	
	InputStream in = blob.getBinaryStream();
	OutputStream output = new FileOutputStream(request.getSession().getServletContext().getRealPath("/")+"Resource/abc.jpg");
	byte[] buffer = new byte[1024];
	int length = 0;
	//没有了返回-1
	while((length = in.read(buffer))!=-1){
		out.println(length);
		output.write(buffer,0,length);
	}
	in.close();
	output.close();

 

JDBC数据库连接池

JBCP:

/**
	 * dbcp数据库连接池的连接数据库的测试
	 * @throws SQLException
	 */
	@Test
	public void dbcpTest() throws SQLException {
		//创建DataSource的实现类
		BasicDataSource dataSource = new BasicDataSource();
		
		//给数据库连接池dbcp提供连接数据库的必须的基本信息
		dataSource.setUsername("root");
		dataSource.setPassword("root");
		dataSource.setUrl("jdbc:mysql://localhost:3306/jdbc_1");
		dataSource.setDriverClassName("com.mysql.jdbc.Driver");
		
		//设置可选的数据库连接池的一些属性,这些属性就是对数据库连接进行管理
		dataSource.setInitialSize(10);//初始化的连接数
		dataSource.setMaxActive(50);//同一时刻可申请的最大连接数
		dataSource.setMinIdle(5);//保存的最少的空闲连接数
		dataSource.setMaxWait(1000*5);//等待数据库连接池分配的最长时间
		
		//可获得到数据库的连接对象
		Connection conn= dataSource.getConnection();
		System.out.println(conn);
/**
	 * dbcp数据库连接池的配置信息和代码分离的用法,BasicDataSourceFactory类
	 * @throws Exception
	 */
	@Test
	public void dbcpFactoryTest() throws Exception {
		Properties pop = new Properties();
		InputStream in = DBCPTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
		pop.load(in);
		//获取BasicDataSource类的对象不用new
		DataSource dataSource = BasicDataSourceFactory.createDataSource(pop);
		//注意,传进去的配置文件中,键值对中“键名”一定要和dbcp包中的setXxx方法中的xxx一致
		
		//通过连接池,获取连接对象
		Connection conn = dataSource.getConnection();
		System.out.println(conn);
	}

c3p0(项目中推荐使用的连接池)

DataSource dataSource = new ComboPooledDataSource("mysql"); //xml 配置文件中名字为mysql
Connection conn = dataSource.getConnection();
System.out.println(conn);

 

DBUtils工具类的使用

1.增\改\删直接上代码,工具类,就是会用就OK

a.获取的QueryRunner类的对象

b.写sql,调用update方法执行呗!

 

2.查询

ResultSetHandler接口:

query方法的返回值就是ResultSetHandler接口里handle方法的返回值

 

BeanHandler类的使用:

把结果集中第一条数据封装成BeanHandler对象指定的Class类的对象返回

 

BeanListHandler类的使用:

把结果集转换为List集合返回,一定有List返回,哪怕list集合里没有元素

 

MapHandler类的使用:

把结果集的第一条数据以Map的形式返回,其中"键"是列名,"值"是列对应的数值

 

MapListHandler类的使用:

把结果集先封装到Map里,在将Map封装到List集合里返回

 

ScalarHandler类的使用:

返回一个数值的sql查询getOneCloumn, count(id),2,username

 

JDBC调用存储过程和函数

建立存储过程的SQL语句:

CREATE PROCEDURE all_user() SELECT * FROM `t_user`;

CREATE PROCEDURE insert_user(IN username VARCHAR(20),IN `password` VARCHAR(50)) INSERT INTO `t_user`(`username`,`password`) VALUES(username,`password`);

CREATE PROCEDURE select_username(IN id INT(11),OUT username VARCHAR(20)) SELECT u.username INTO username FROM `t_user` u WHERE u.id=id;
/**
	 * 测试没有参数的存储过程的调用
	 */
	@Test
	void CallableTest() {
		Connection conn = null;
		ResultSet rs = null;
		CallableStatement stat = null;
		try {
			conn = MyDBUtils.getConnection();
			stat = conn.prepareCall("{call all_user()}");
			rs = stat.executeQuery();
			User user = null;
			List<User> list = new ArrayList<>();
			while(rs.next()) {
				user = new User();
				user.setId(rs.getInt("id"));
				user.setPassword(rs.getString("password"));
				user.setUserName(rs.getString("username"));
				list.add(user);
			}
			System.out.println(list);
		} catch (Exception e) {
			
		}finally {
			MyDBUtils.close(conn, stat, rs);
		}
	}
/**
	 * 测试有in参数的存储过程的调用
	 */
	@Test
	void CallableTest1() {
		Connection conn = null;
		CallableStatement stat = null;
		try {
			conn = MyDBUtils.getConnection();
			stat = conn.prepareCall("{call insert_user(?,?)}");
			stat.setString(1, "linlinlin");
			stat.setString(2, "mima");
			stat.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			MyDBUtils.close(conn, stat, null);
		}
	}
/**
	 * 测试有out参数的存储过程的调用
	 */
	@Test
	void CallableTest2() {
		Connection conn = null;
		CallableStatement stat = null;
		ResultSet rs = null;
		try {
			conn = MyDBUtils.getConnection();
			stat = conn.prepareCall("{call select_username(?,?)}");
			stat.setInt(1, 2);
			//out类型的参数需要我们注册
			stat.registerOutParameter(2,Types.VARCHAR);
			//out类型的参数注册完后还要更新
			stat.execute();//真正执行存储过程的SQL语句
			//根据out参数的索引值取出存储过程返回回来的值
			String username = stat.getString(2);
			stat.executeUpdate();
			System.out.println(username);
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			MyDBUtils.close(conn, stat, rs);
		}
	}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值