Marco's Java 之【JDBC基础】

JDBC

JDBC(Java Data Base Connectivity):Java数据库连接,它是一种用于执行SQL语句的Java API,为多种关系数据库提供统一访问。JDBC是Java提供的一种规范,还是一种协议,它提供了连接SQL数据库的接口,由数据库的各大厂商去实现该接口,实现java和数据库之间的连接。

连接数据库的步骤:

//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.创建数据库连接
Connection conn = DriverMannager.getConnection("jdbc:mysql://host:port/database","user","password");
//3.操作数据库对象,将sql发送给数据库
Statement  stat = conn.createStatement();
//4.执行sql语句
stat.execute(sql);//返回的是一个布尔类型的值,操作成功为true,失败为false
//5.释放资源
stat.close();
conn.close();

关于getConnection()中的URL分为几个部分:jdbc:mysql指定了使用什么数据库,127.0.0.1是指需要连接的数据库的IP地址,3306则是数据库的端口号,department是数据库名称,useUnicode=true&characterEncoding=UTF8这一部分则是指定字符集,避免后期传入数据到数据库中产生乱码数据

jdbc:mysql://127.0.0.1:3306/department?useUnicode=true&characterEncoding=UTF8

注意: 建立连接对象内部其实包含了Socket对象,是一个远程的连接,比较耗时,因此真正开发时,为了提高效率会建立连接池来管理连接对象

PreparedStatement(预编译数据库操作对象)
StatementPreparedStatement
可读性差,需要拼接字符串高,可单独设置每个变量,操作比较灵活
预编译普通sql无预编译可配置开启预编译,会提高操作性能
安全性差,可能被sql注入到拼接字段强,sql提前预编译,传入的参数中的字符串,如果有特殊字符会被转义
String sql3 = "insert into department values (0,?,?,?, now())";
PreparedStatement ps = conn.prepareStatement(sql3);
ps.setObject(1, "sales");
ps.setObject(2, "sale products");
java.sql.Date date = new java.sql.Date(System.currentTimeMillis());

SQL注入攻击
在JDBC操作数据库的时候,是使用SQL拼接的方式来进行操作的,基于这一点,一些用户通过输入特殊的字符,强行的注入一些信息,强行操作数据库,这就是SQL注入,比如用户名输入‘or 1 = 1 --

>> 假设我们后台操作有这样的语句
String sql = "select * from user_table where username=' "
	+ userName + " ' and password=' "+ password +" '";
>> 当有恶意的人员输入账户密码之后
  select * from t_user where username=
  ''or 1 = 1 -- and password=''

此时我们可以发现username = ' ' 什么也没写,1=1又是恒成立的,并且用--将密码注释掉,语句永远可以正确的执行,此时我们就可以对数据库做任何操作,比如删库!!

select* from user_table where
	username='' ;DROP DATABASE (databaseName) --' and password=''

而采用PreparedStatement预编译方式可以有效地避免注入攻击,原因是当我们使用setObject()等方法对?部分传参是,PreparedStatement的底层对传入的参数进行转义了,那么我们传入的数据将被当作是一个字符串整体结构,因此不会像Statement一样对数据进行拼接,在一定程度上规避了SQL注入

ResultSet(结果集)

ResultSet对象具有指向当前数据的光标,调用next方法可以将光标移到下一行,这和我们集合中使用的Iterator迭代器很类似。其实ResultSet中就相当是一个集合,存储的是我们查询数据库返回的结果集

String sql = "select * from student";
ResultSet rs = stmt.excuteQuery(sql);
Int id = rs.getInt(1);//获取第一行字段的信息(id)
String name = rs.getString(2);//获取第一行字段的信息(name)
Date emtDate = rs.getDate(3);//获取第一行字段的信息(enrollment date)

这么写大家应该发现一个问题,就是如果一张表中有多个字段,那么我们如果需要获取其中一行的数据还需要一个个的去数,才能拿到对应的值,因此,使用下面这种方式能够更清晰的明确你需要的是什么数据,且可以减少不必要的错误(例如数列数的时候数错了)。

Int id = rs.getInt(id);//获取id字段的信息(id)
String name = rs.getString(name);//获取name字段的信息(name)
Date emtDate = rs.getDate(enrollment);//获取enrollment date字段的信息(enrollment date)

批量导入(Add_batch)

public class BatchTest {
	public static void main(String[] args) {
		Connection conn = null;
		Statement stmt = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
			String url = "jdbc:mysql://127.0.0.1:3306/department?useUnicode = true & characterEncoding = UTF-8";
			conn = DriverManager.getConnection(url, "root", "********");
			conn.setAutoCommit(false);
			stmt = conn.createStatement();	
			for(int i = 0; i < 20000; i++) {
				stmt.addBatch("insert into worker(w_name,w_age,w_hiredate) values ('marco" + i + "',20,now())");//使用批量导入的效率比一次次的导入效率更高
			}
			int[] list = stmt.executeBatch();
			conn.commit();
			System.out.println(Arrays.toString(list));
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			if(stmt != null) {
				try {
					stmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if(conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}		
	}
}

注意:
1.一般使用Statement来做批量处理,而不使用 PreparedStatement
2. 事务一定要设为手动提交conn.setAutoCommit(false)

现在我们通过以上的内容做一个简单的案例,将Java和Mysql数据库连接起来,并将Mysql数据库中的操作封装成Java中的方法,首先我们创建一个表 worker

CREATE TABLE `worker` (
  `w_id` int(10) NOT NULL AUTO_INCREMENT,
  `w_age` int(10) NOT NULL,
  `w_name` varchar(20) COLLATE utf8mb4_bin NOT NULL,
  `w_hiredate` datetime DEFAULT NULL,
  PRIMARY KEY (`w_id`)
) ENGINE=InnoDB AUTO_INCREMENT=40004 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

现在我想通过Java来将数据传输到指定的数据库,首先先完成数据库的基本信息配置
通过Properties将数据持久化保存在properties文件中

public class DatabaseConfig {
	private static Properties pro = new Properties();
	public static void setConfig() {
		String url = "jdbc:mysql://127.0.0.1:3306/department?useUnicode=true& characterEncoding = UTF-8";
		String user = "root";
		String password = "123456";
		pro.setProperty("URL", url);
		pro.setProperty("USER", user);
		pro.setProperty("PASSWORD", password);
		try {
			FileOutputStream fos = new FileOutputStream("databaseconfig.txt");
			pro.store(fos, "URL+USER+PASSWORD");
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("Storing successfully!!");
	}
	public static void main(String[] args) {
		setConfig();//配置数据库基本配置信息
	}
}

然后我们来定义一个JDBCUtil工具类,用来提供创建Connection连接对象的方法,和释放资源的方法

public class JDBCUtil {
	private static Properties pro = new Properties();
	//获取操作数据库对象
	public static Connection getConnection() {
		try {
			Class.forName("com.mysql.jdbc.Driver");
			pro.load(new FileInputStream("databaseconfig.txt"));	
			String url = (String) pro.get("URL");
			String user = (String) pro.get("USER");
			String password = (String) pro.get("PASSWORD");
			Connection conn = DriverManager.getConnection(url,user,password);
			return conn;
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	//释放资源
	public static void realse(Connection conn, Statement stmt) {
		if(conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if(stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	//释放资源方法重载
	public static void realse(Connection conn, Statement stmt, ResultSet rs) {
		if(rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		realse(conn, stmt);
	}
}

最后创建Jdbc类,提供addData、deleteData、updateData的基本方法

public class Jdbc {
	public static void main(String[] args) {
		new Jdbc().addData(20, "marco");
		new Jdbc().deleteData(40001);
		new Jdbc().updateData(40003, 20, "Marco");	
		new Jdbc().searchData();	
	}
	//添加数据到指定数据库
	public void addData(int age, String name) {
		Connection conn = JDBCUtil.getConnection();
		Statement stmt = null;
		try {
			//方式一:创建Statement操控数据库对象
			stmt = conn.createStatement();
			String sql = "insert into worker (w_age,w_name,w_hiredate) 
				values (" + age + ",'" + name + "', now())";
			stmt.execute(sql);
			//方式二:创建prepareStatement操控数据库对象(更加灵活)
			String sql = "insert into worker (w_age,w_name,w_hiredate) values (?,?,?)";
			ps = conn.prepareStatement(sql);
			ps.setObject(1, age);
			ps.setObject(2, name);
			ps.setObject(3, new java.sql.Date(System.currentTimeMillis()));
			ps.execute();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		JDBCUtil.realse(conn, stmt);
	}
	//删除某行数据库中的内容
	public void deleteData(int id) {
		Connection conn = JDBCUtil.getConnection();
		Statement stmt = null;
		try {
			stmt = conn.createStatement();
			String sql = "delete from worker where w_id  = " + id;
			stmt.execute(sql);
		} catch (SQLException e) {
			e.printStackTrace();
		}	
		JDBCUtil.realse(conn, stmt);
	}
	//更新数据库
	public void updateData(int id, int age, String name) {
		Connection conn = JDBCUtil.getConnection();
		Statement stmt = null;
		try {
			stmt = conn.createStatement();
			String sql = "update worker set w_age = " + age + 
				",w_name = '" + name + "' where w_id =" + id;
			stmt.execute(sql);
		} catch (SQLException e) {
			e.printStackTrace();
		}	
		JDBCUtil.realse(conn, stmt);
	}
	//查找所有数据(当然如果有需求也可以另行传参定义新的方法)
	public void searchData() {
		Connection conn = JDBCUtil.getConnection();
		PreparedStatement ps = null;
		try {
			String sql = "select * from worker";
			ps = conn.prepareStatement(sql);
			ResultSet rs = ps.executeQuery();
			while(rs.next()) {
				System.out.println(rs.getObject(1) + "\t" + 
					rs.getInt(2) + "\t" + rs.getString(3) + "\t" + rs.getDate(4));
			}		
		} catch (SQLException e) {
			e.printStackTrace();
		}	
		JDBCUtil.realse(conn, ps, rs);
	}
}

通过上述案例,我们将数据库里所需要执行的操作通过Java的方法封装起来,当我们需要对数据库的数据做修改的时候,只需要调用封装好的方法即可,实现了模块之间的数据连接。

但是上述案例中的查询语句只能查询我们固定好的表 worker,但是在实际开发中,我们可能会需要查询这一个数据库中的多个表,并且将表中的数据导出来
那么我们可以通过将查询的内容进行封装,在Java中创建一个实体类让类中的属性和数据库中的字段对应起来
首先我需要创建一个User类(当然大家也可以自定义类和对应的表),我这边给User设定的属性一共四个,并给这个Javabean类一个全参构造器和相应的get,set方法,以及重写toString方法
private String userName; private String password; private String telePhone; private String realName;

public class ResultMetaTest{
	public static <T> List<T>  queryData(Class<T> clz, String sql, Object...param) {
		Connection conn = JDBCUtil.getConnection();//创建连接对象
		
		try {
			PreparedStatement ps = conn.prepareStatement(sql);//创建操作数据库对象
			//设置条件判断中的值
			for(int i = 0; i < param.length; i++) {
				ps.setObject(i+1, param[i]);
			}
			ResultSet rs = ps.executeQuery();//获取结果集对象
			ResultSetMetaData meta = rs.getMetaData();//获取数据集元信息
			int column = meta.getColumnCount();//获取所搜寻的字段个数或者列数
			List<T> list = new ArrayList<>();//创建用于储存构建出来的对象的容器
			while(rs.next()) {
				Map<String,Object> map = new HashMap<>();//创建用于储存保存类的属性的map集合
				for(int i = 0; i < column; i++) {
					String columnName = meta.getColumnLabel(i+1);//得到每一列的或者每一个字段的名称
					Object obj = rs.getObject(columnName);//根据字段名称获取每一行对应的字段值
					map.put(columnName, obj);
				}
				if(!map.isEmpty()) {	
					T obj = clz.newInstance();//通过传入的类的Class对象创建这个类的对象
					Set <Entry <String,Object>> entryset = map.entrySet();
					for (Entry<String, Object> entry : entryset) {
						String atribute = entry.getKey();//提取这个类中的属性名称
						Object value = entry.getValue();//提取这个类中属性的值
						Field attribute = clz.getDeclaredField(atribute);
						attribute.setAccessible(true);//注意要设置权限为开放
						attribute.set(obj, value);//通过反射给对象赋值
					}
					list.add(obj);//将赋值完成的对象放入ArrayList容器中
				}				
			}
			JDBCUtil.realse(conn, ps, rs);//释放资源
			return list;
		} catch (SQLException | InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static void main(String[] args) throws Exception {
		String sql = "select u_uname as userName,u_pwd as password,"
				+ "u_tele as telePhone,u_realname as realName from userinfo where u_id = ?";
		List<User> list = queryData(User.class,sql,1);
		System.out.println(list);
	}
}

输出结果如下 >>>
result
Marco’s Java 之【JDBC辅助类封装】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值