Java中的JDBC(详解、带例子)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_44296929/article/details/102722276

JDBC: (摘自百度百科)

一、 JDBC(Java DataBase Connectivity,Java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成,JDBC提供一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能编写数据库应用程序。
在这里插入图片描述
也就是说因为Java程序员需要连接多种数据库,为了避免每一种数据库都学习一套新的api,Sun公司提出了一个JDBC的接口,各个数据库的厂商根据此接口写实现类(驱动),这样Java程序员只需要掌握JDBC接口的调用,即可访问任何数据库。

二、执行JDBC的流程:(下面会逐步进行解释)

  1. 注册驱动(加载数据库驱动)
  2. 获取连接对象(Connection)
  3. 创建SQL执行对象(Statement)
  4. 执行SQL语句
  5. 关闭资源

三、JDBC核心类(接口)介绍:

JDBC的核心类有:DriverManagerConnectionStatementResultSet

1、DriverManager类 驱动管理器,是管理一组JDBC驱动程序的基本服务,主要是用于 注册驱动获取连接

a、注册驱动 :这可以让JDBC接口知道连接的是哪个驱动(也就是连接哪个数据库)。
Class.forName(“全限定名(包名+类名)”)

Class.forName("com.mysql.jdbc.Driver"); 	//Mysql
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");	//SQL server

b、获取连接对象:通过DriverManager的api来获取连接对象。
Connection DriverManager.getConnection(url,username,password);

常用数据库URL地址的写法:
MySql:jdbc:mysql://localhost:3306/数据库名称
Oracle:jdbc:oracle:thin:@localhost:1521/数据库名称
SqlServer:jdbc:microsoft:sqlserver://localhost:1433/数据库名称

参数的含义:
url:用于标识数据库的位置,要连接的是什么数据库。
url格式:协议:子协议:子协议名称:IP地址:端口号:数据库名称(前部分是数据库厂商规定的)
username:是我们要连接的数据库的登陆名
password:使我们要登陆用户的对应密码(如果没设置可以为空)


2、Connection 接口:如果可以获取到Connection对象,那么说明已经与数据库连接上了,Connection对象表示连接,与数据库的通讯录都是通过这个对象展开的:Connection最重要的一个方法就是用来获取Statement对象和PreparedStatement对象;

常用方法:
a、创建Statement -语句执行者:Statement createStatement();

Statement st = null;
//获取用于向数据库发送sql语句的statement
st = conn.createStatement();
//执行SQL语句
String sql = "select idd,username,password from users";
st.executeQuery(sql);

b、创建一个预编译的语句执行对象:PreparedStatement prepareStatement(String sql);

PreperedStatement st = null;
String sql = "select * from users where username=? and password=?";
//获取用于向数据库发送sql语句的Preperedstatement
st = conn.preparedStatement(sql);//在此次传入,进行预编译
st.setString(1, username);
st.setString(2, password);
//执行SQL语句
st.executeQuery();//在这里不需要传入sql

c、创建一个 CallableStatement 对象来调用数据库存储过程(了解):CallableStatement prepareCall(String sql);

注:比较Statement和PreperedStatement对象:
1、相对于Statement对象而言PreperedStatement可以避免SQL注入问题。(下面例子会详细的解释)
如:String sql=“select * from users where name=’”+loginName+"’ and pwd=’"+loginPwd+"’";
实际上发送:select * from admin where loginname=‘333’ and loginpwd=‘wer’or’1’=‘1’,登录成功!
因为这条SQL语句后面有一个or 1=1 则这条语句一定会成功其实并没有输入真正的密码,所以SQL注入成功。
2、Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。PreparedStatement 可对SQL进行预编译,从而提高数据库的执行效率。
3、并且PreperedStatement对于sql中的参数,允许使用占位符的形式进行替换,简化sql语句的编写。


3、Statement接口: Statement对象是SQL语句的执行者,是用来向数据库发送SQL语句的。

常用方法:
a、执行查询语句,返回一个集合:ResultSet executeQuery(String sql)

b、执行更新 插入 删除语句,返回影响行数:int executeUpdate(String sql)

c、执行给定的 SQL 语句,该语句可能返回多个结果:boolean execute(sql)
注:该方法返回值不同,使用方式也不同:返回值为true时,表示执行的查询语句,使用getResultSet方法获取结果;返回值为false时,表示执行更新或DDL语句,使用getUpdateCount获取结果。

d、把多条sql语句放到一个批处理中:addBatch(String sql),然后通过executeBatch()向数据库发送一批sql语句执行。


4、ResultSet接口: ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式,ResultSet 对象维护了一个指针,初始的时候,指针指向的是结果集的第一行,可以通过提供的api对指针进行操作,查看结果集。

1、关于ResultSet对象对指针操作的方法:

  • next():移动到下一行
  • Previous():移动到前一行
  • absolute(int row):移动到指定行
  • beforeFirst():移动resultSet的最前面
  • afterLast() :移动到resultSet的最后面

2、ResultSet也提供了一些直接获取值的方法:

获取任意类型的数据:

  • getObject(int index)
  • getObject(string columnName)

获取指定类型的数据,例如:

  • getString(int index)
  • getString(String columnName)

常用数据类型转换:
在这里插入图片描述
例如:我们最后会整体的执行一遍(下面只是一个例子)

ResultSet rs = null;
//执行sql语句,并获取结果集
String sql = "select idd,username,password from users";
rs = st.executeQuery(sql);
//取出结果集的数据
rs.afterLast();
rs.previous();
System.out.println("idd=" + rs.getObject("idd"));
System.out.println("username=" + rs.getObject("username"));
System.out.println("password=" + rs.getObject("password"));

四、整体流程: 我们这里是使用的mysql数据库

1、首先创建Maven项目,然后在pom.xml添加依赖。

<dependencies>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.6</version>
		</dependency>
		<!-- 数据库连接池,后续会用到 -->
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
		</dependency>
	</dependencies>

2、我们添加一个配置文件jdbc.properties放在src/resources,配置数据库的连接信息。

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/数据库名称
username=填写你的登陆名
password=填写你的登陆密码

3、添加一个class类,添加一个静态块,然后读取配置文件。通常情况下,这个过程是每次都要执行的,所以一般都封装成一个DBUtil类,然后每次直接调用就行了。

public class DBUtils {
	private static String driver;
	private static String url;
	private static String username;
	private static String password;
	static{
		//创建一个读取配置文件的属性对象
		Properties prop=new Properties();
		//获取文件的输入流
		InputStreamips=DBUtils.class.getClassLoader()
					.getResourceAsStream("jdbc.properties");
		try {
			//把文件加载到属性对象中
			prop.load(ips);
			//获取数据
			driver=prop.getProperty("driver");
			url=prop.getProperty("url");
			username=prop.getProperty("username");
			password=prop.getProperty("password");
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				//关闭流
				ips.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

4、注册驱动,获取连接,我们将这两步封装在DBUtils中的一个getConn()方法中,便于后续调用。

public static Connection getConn() throws Exception{
		//注册驱动
		Class.forName(driver);
		//获取连接
		Connection conn =DriverManager.getConnection(url,username,password);
		return conn;
	}

5、关闭资源,我们也封装为DBUtils的一个close()方法。

//关闭资源
public static void close(Connection conn,Statement stat,ResultSet rs){
	try {
		//判断有值时关闭
		if(rs!=null){
			rs.close();
		}
	} catch (SQLException e) {
		e.printStackTrace();
	}
	try {
		if(stat!=null){
			stat.close();
		}
	} catch (SQLException e) {
		e.printStackTrace();
	}
	try {
		if(conn!=null){
			conn.close();
		}
	} catch (SQLException e) {
		e.printStackTrace();
	}
}

到此我们就将DBUtils类封装好了,我们后续直接调用就可以了。

五、对数据库进行操作:(代码中加了注释,直接看代码就行)
1、插入操作:(使用的是excuteUpdate执行SQL 返回值为int)

@Test
public void insert(){
	//声明连接对象Connection和执行Sql的Statement对象
	Connection conn=null;
	Statement stat=null;
	try{
		conn=DBUtils.getConn();	 //调用上面我们封装的获取Connection对象的方法
		stat=conn.createStatement();	//通过Connection获取Statement对象
		String sql="insert into jdbc values(1,'Tom')";	//创建一条SQL语句
		stat.executeUpdate(sql);	//执行SQL语句(这里也可以接受一下int返回值,查看受影响条数)
	}catch(Exception e){
		e.printStackTrace();
	}finally{
		DBUtils.close(conn, stat, null);	//最后调用我们封装好的close方法关闭资源
	}
}

2、更新操作:(这里和上面基本流程相同)

@Test
public void update(){
	Connection conn = null;
	Statement stat = null;
	try {
		conn = DBUtils.getConn();
		stat = conn.createStatement();
		String sql="update jdbc set name='Jerry' where id=1";
		stat.executeUpdate(sql);
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		DBUtils.close(conn, stat, rs);
	}
}

3、删除操作:(这里也是用的是excuteUpdate返回int 受影响行数)

@Test
public void delete(){
	Connection conn = null;
	Statement stat = null;
	ResultSet rs = null;
	try {
		conn = DBUtils.getConn();
		stat = conn.createStatement();
		String sql="delete from jdbc where id=1";
		stat.executeUpdate(sql);
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		DBUtils.close(conn, stat, rs);
	}
}

4、查询操作:(这里使用的是excuteQuery 返回值为ResultSet一个结果集)

@Test
public void select(){
	Connection conn = null;
	Statement stat = null;
	ResultSet rs = null;	//这里多声明一个结果集对象ResultSet
	try {
		conn = DBUtils.getConn();
		stat=conn.createStatement();
		rs=stat.executeQuery("select * from emp");	//执行查询并获取结果集
		while(rs.next()){		//使用next()方法逐条遍历所有结果
			String name=rs.getString("ename");
			double sal=rs.getDouble("sal");
			System.out.println(name+" "+sal);
		}
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		DBUtils.close(conn, stat, rs);	//关闭资源
	}
}

六、使用数据库连接池:(优化过程)
1、前几步和上面第四节相同,只不过多添加一下连接池的依赖,上面也已经添加过了,下面将DBUtils类进行一部分更改即可。

2、首先是上面的static静态块更改:(这里直接将数据源配置好)

public class DBUtils {
	private static String driver;
	private static String url;
	private static String username;
	private static String password;
	private static BasicDataSource dataSource;	//声明数据源,用于配置数据库的链接信息
	static{
		Properties prop=new Properties();
		InputStream ips=DBUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
		try {
			prop.load(ips);	//加载配置文件
			driver=prop.getProperty("driver");	//获取我们在配置文件中配置的driver等信息
			url=prop.getProperty("url");
			username=prop.getProperty("username");
			password=prop.getProperty("password");
			//创建数据源
			dataSource=new BasicDataSource();	//创建一个数据源
			//设置数据库的连接信息:
			dataSource.setDriverClassName(driver);
			dataSource.setUrl(url);
			dataSource.setUsername(username);
			dataSource.setPassword(password);
			//设置初始连接数量
			dataSource.setInitialSize(3);
			//设置最大连接数量
			dataSource.setMaxActive(3);
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				ips.close();	//关闭资源
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

3、修改一下获取连接对象Connection的方法:(因为上面已经配置好了,我们直接通过数据源获取Conn就行了)

public static Connection getConn() throws Exception{
	/*Class.forName(driver);
	Connection conn=DriverManager.getConnection(url,username,password);*/
	//从连接池中获取连接
	Connection conn=dataSource.getConnection();
	return conn;
}

4、修改close方法:(这里将自动提交打开)

public static void close(Connection conn,Statement stat,ResultSet rs){
	try {
		if(rs!=null){
			rs.close();
		}
	} catch (SQLException e) {
		e.printStackTrace();
	}
	try {
		if(stat!=null){
			stat.close();
		}
	} catch (SQLException e) {
		e.printStackTrace();
	}
	try {
		if(conn!=null){
			//打开自动提交
			conn.setAutoCommit(true);
			conn.close();
		}
	} catch (SQLException e) {
		e.printStackTrace();
	}
}

5、执行查询操作:

@Test
public void select(){
	Connection conn = null;
	Statement stat = null;
	ResultSet rs = null;
	try {
		conn = DBUtils.getConn();
		stat = conn.createStatement();
		String sql="select * from emp";
		rs=stat.executeQuery(sql);
		while(rs.next()){	//这里的方法上面都有介绍
			int empno=rs.getInt("empno");	//获取不同类型的参数(通过参数名)
			String name=rs.getString("ename");
			double sal=rs.getDouble("sal");
			System.out.println(empno+" "+name+" "+sal);
		}
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		DBUtils.close(conn, stat, rs);	//别忘了关闭资源
	}
}

6、插入操作:我们这里使用的是PrepareStatement对象(下面实现通过控制台输入的用户名和年龄进行插入,不多说了看注释吧)

public static void main(String[] args) {
	Scanner scan=new Scanner(System.in);
	System.out.println("请输入姓名");
	String name=scan.nextLine();	//接收用户输入的用户名
	Scanner scr=new Scanner(System.in);
	System.out.println("请输入年龄");
	int age=Integer.parseInt(scr.nextLine());	//接收用户输入的年龄
	//2.把得到的内容拼接到sql语句中
	String sql="insert into jdbcuser values(?,?)";	//创建一条SQL,保留两个参数
	Connection conn = null;		//声明连接对象
	PreparedStatement stat = null;	//声明预加载SQL执行对象
	ResultSet rs = null;	//声明结果集对象
	try {
		conn = DBUtils.getConn();	//获取连接
		stat = conn.prepareStatement(sql);	//获取预加载SQL执行对象
		//把?换回正确的内容
		stat.setString(1, name);
		stat.setInt(2, age);
		stat.executeUpdate();	//这里不要再添加SQL语句了,上面也说过了
		System.out.println("插入成功");
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		DBUtils.close(conn, stat, rs);	//关闭资源
	}
}

7、SQL注入问题:(使用Statement对象存在SQL注入问题)
控制台接收用户输入的用户名和密码,验证以后看是否登陆成功。结果后面加上 1 = 1 以后肯定为true,所以不论我们前面验证是否成功,所以都会登陆成功,也就是我们通过后期输入参数的形式进行了SQL注入,这样可能会发生危险。

public static void main(String[] args) {
	Scanner scan=new Scanner(System.in);
	System.out.println("请输入用户名");
	String username=scan.nextLine();
	Scanner scr=new Scanner(System.in);
	System.out.println("请输入密码");
	String password=scr.nextLine();
	boolean b=login(username,password);
	if(b){
		System.out.println("登陆成功");
	}else{
		System.out.println("登录失败");
	}
}
//' or '1'='1   SQL注入改变原有SQL语句的逻辑导致什么都可以登陆成功
private static boolean login(String username, String password) {
	String sql="select count(*) from user where username='"+username+"' and password='"+password+"'";
	Connection conn = null;
	Statement stat = null;
	ResultSet rs = null;
	try {
		conn = DBUtils.getConn();
		stat = conn.createStatement();
		rs=stat.executeQuery(sql);
		while(rs.next()){
			//得到查询的数量
			int count=rs.getInt(1);
			if(count>0){
				return true;
			}
		}
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		DBUtils.close(conn, stat, rs);
	}
	return false;
}

8、使用PrepareStatement来实现登陆验证:

public static void main(String[] args) {
	Scanner scan=new Scanner(System.in);
	System.out.println("请输入用户名");
	String username=scan.nextLine();
	Scanner scr=new Scanner(System.in);
	System.out.println("请输入密码");
	String password=scr.nextLine();
	boolean b=login(username,password);
	if(b){
		System.out.println("登陆成功");
	}else{
		System.out.println("登录失败");
	}
}
private static boolean login(String username, String password) {
	String sql="select count(*) from user where username=? and password=?";
	Connection conn = null;
	PreparedStatement stat = null;
	ResultSet rs = null;
	try {
		conn = DBUtils.getConn();
		stat = conn.prepareStatement(sql);
		stat.setString(1, username);
		stat.setString(2, password);
		rs=stat.executeQuery();
		while(rs.next()){
			//得到查询的数量
			int count=rs.getInt(1);
			if(count>0){
				return true;
			}
		}
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		DBUtils.close(conn, stat, rs);
	}
	return false;
}

9、批量操作(使用addBatch):使用Statement对象:

@Test
public void test01(){
	String sql1="insert into user values(null,'悟空','aaa')";
	String sql2="insert into user values(null,'八戒','bbb')";
	String sql3="insert into user values(null,'沙僧','ccc')";
	Connection conn = null;
	Statement stat = null;
	ResultSet rs = null;
	try {
		conn = DBUtils.getConn();
		stat = conn.createStatement();
		stat.addBatch(sql1);
		stat.addBatch(sql2);
		stat.addBatch(sql3);
		//执行批量操作
		stat.executeBatch();
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		DBUtils.close(conn, stat, rs);
	}
}

10、批量操作(使用addBatch):使用PrepareStatement对象:

@Test
public void test02(){
	String sql="insert into user values(null,?,?)";
	Connection conn = null;
	PreparedStatement stat = null;
	ResultSet rs = null;
	try {
		conn = DBUtils.getConn();
		stat = conn.prepareStatement(sql);
		stat.setString(1, "刘备");
		stat.setString(2, "aaa");
		//添加到批量处理
		stat.addBatch();
		stat.setString(1, "关羽");
		stat.setString(2, "bbb");
		stat.addBatch();
		stat.setString(1, "张飞");
		stat.setString(2, "ccc");
		stat.addBatch();
		stat.executeBatch();
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		DBUtils.close(conn, stat, rs);
	}
}

11、批量操作(使用addBatch):避免内存溢出:

@Test
public void test03(){
	String sql="insert into user values(null,?,?)";
	Connection conn = null;
	PreparedStatement stat = null;
	ResultSet rs = null;
	try {
		conn = DBUtils.getConn();
		stat = conn.prepareStatement(sql);
		for(int i=0;i<100;i++){
			stat.setString(1, "name"+i);
			stat.setString(2, "admin");
			stat.addBatch();
			//下面写法可以避免内存溢出
			if(i%20==0){
				stat.executeBatch();
				//清除批处理内容
				stat.clearBatch();
			}
		}
		stat.executeBatch();
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		DBUtils.close(conn, stat, rs);
	}
}

12、自己手动设置主键:获取主键

public static void main(String[] args) {
	Connection conn = null;
	Statement stat = null;
	ResultSet rs = null;
	try {
		conn = DBUtils.getConn();
		stat = conn.createStatement();
		String sql="insert into user values(null,'刘德华','abc')";
		//执行SQL并且制定需要获取自增主键值
		stat.executeUpdate(sql,Statement.RETURN_GENERATED_KEYS);
		//获取返回的主键值
		rs=stat.getGeneratedKeys();
		while(rs.next()){
			int id=rs.getInt(1);
			System.out.println("自增主键"+id);
		}
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		DBUtils.close(conn, stat, rs);
	}
}

其实JDBC的这些类和接口封装了好多的方法,有兴趣可以自己去看看源代码,举的例子也够多的了,其实掌握上面的内容完全足够用了。

上面就是所有关于JDBC的内容了,希望能对你有所帮助,如果喜欢的话那就点个赞吧,或者点个关注支持一下!!谢谢!!

猜你喜欢

转载自blog.csdn.net/weixin_44296929/article/details/102722276
今日推荐