目录
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);
}
}