JDBC
jdbc: java连接数据库的一套规范
各个数据库实现这一套规范,就是该数据库的驱动jar包(驱动jar包的版本号与数据库的版本号不是一一对应,可以互相兼容的)
jdbc与数据库驱动的关系就是接口与实现的关系
类加载器加载类里面的文件?
inputStream = MyConnection.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(inputStream);
所以java连接数据库:
第一步,导入要连接的数据库的驱动jar包,
第二步,代码加载驱动jar包
使用反射加载, Class.forName("com.mysql.jdbc.Driver") ,耦合度低,只加载一次
基本不这样写:1) DriverManager.registerDiver(new Driver()); 因为在mysql实现的驱动Driver里的静态块已经注册了驱动,new的时候静态块加载一次,
registerDiver又加载一次,导致mysql底层会注册两次
2) new Driver() -----》 可以省略DriverManager.registerDiver,但是当前程序就跟Mysql驱动绑定,以后不能没有扩展性,耦合度比较高
核心对象:
DriverManager 用于注册驱动 静态工具类
Connection 表示与数据库创建的连接 DriverManager.getConnection
Statement 操作数据库sql语句的对象 connection.createStatement(); --- 使用PreparedStatement替代它
ResultSet 结果集或一张虚拟表 statement.getResultSet()
除了next,不要使用ResultSet的方法遍历,
可以转化为list,map再遍历
更好的是使用jdk8的stream流式编程(jdk8,lambda表达式、stream流式编程)
resultSET,没有查询到结果,返回值有的,不是null
---ResultSet 对象没有下一行时返回 false
由于Statement的执行效率低,并且sql拼接会出现sql的注入,所以引出了:
PreparedStatement,预编译的Statement,性能比较高,可以缓存经常执行的sql语句
用占位符的方式去传值,不用拼接字符串,避免了sql注入!
Connection可以多个方法共享,statement不能被多个方法共享
常用的实现框架:
MyBatis: 国内最流行的jdbc封装框架,sql语句写在xml里,返回值转化为bean,不用我们再封装成对象(亲力亲为,但是可以很好的优化)
Hibernate:冬眠的意思,让sql冬眠,面向对象的数据库编程,现在国外比较流行的持久化框架,国内低调
底层封装复杂的sql,难优化,适用于中小型项目
JPA: sun提出面向对象编程的规范(从hibernate学习的规范) ----》 JPA是一个规范,不是框架
有hibernate实现,spring实现(基本spring一统天下)
对jdbc的改进优化有什么想法,提JPA
一般对数据库表数据的查询删除等,都是通过主键id来查询判断的,不要使用name其它字段,除非是业务需求
-----
操作数据库:
idea中,jdbc中executeUpdate( "update cat set age=age+100" );点开document窗口,里面会显示表的数据结构!!!,当然,前提是已经连接了数据库
把sql抽取出来写: String sql = "update cat set age=age+100" ,document窗口,不会会显示表的数据结构,但是数据库可以校验写的表,,,
代码的重构: 把相似的代码提取出来,重复调用
利用继承关系,面向接口编程,即模板模式编程: 子类实现业务代码,父类实现重复的代码!
单元测试: beforeclass,afterclass,before,after的使用
executeUpdate: 用来执行(insert ,update,delete),没有结果集的返回值,只有影响行数的返回值
public class ConnectionTest {
static Connection connection = null;
//如果每个单元测试方法都要使用statement,放到@Before修饰的方法里面,保证每次执行单元测试时候都会初始化这个对象
Statement statement=null;
@BeforeClass
//用来设定每次进行单元测试之前都初始化一次的函数
public static void beforeClass() throws Exception {
Class.forName( "com.mysql.jdbc.Driver" );
//定义连接数据库的url ,用来指定怎么样找到哪个数据库
//格式如下: 协议 子协议 IP :端口号 数据库
String url = "jdbc:mysql://localhost:3306/bg_1904?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC";
String user = "root";
String password = "";
//2.JDBC第二步 通过注册管理器得到数据库的连接Connection
connection = DriverManager.getConnection( url, user, password );
}
@Before
public void before() throws SQLException {
statement = connection.createStatement();
}
@Test
public void testExecuteUpdate() throws SQLException {
//executeUpdate用来执行(insert ,update,delete),没有结果集的返回值,只有影响行数的返回值
int i = statement.executeUpdate( "update cat set age=age+100" );
int j = statement.executeUpdate( "insert into cat values ('jerry',50)" );
statement.executeUpdate( "delete from cat where name='jerry'" );
}
@After
public void after() {
if (statement!=null) {
try {
statement.close();
} catch (SQLException e) {
}
}
}
@AfterClass
public static void afterClass() {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
}
}
}
}
---------
executeQuery : 用来执行select语句,返回值是一个结果集ResultSet;
默认提供一个游标,指向第一行的之前,如果判断当前数据库有没有记录,就要判断next()后有没有数据
ResultSet resultSet = statement.executeQuery( "select name as catName,age as catAge from cat" );
//获取数据的第一种方式:
if (resultSet.next()) {
//getObject严格的说,是根据列的别名来获取值,如果没有别名那就写列名
Object name = resultSet.getObject( "catName" );
System.out.println( "name = " + name );
}
//获取数据的第二种方式:通过在getObject传入列的序号(注意:从1开始)
if (resultSet.next()) {
Object name = resultSet.getObject( 1 );
Object age = resultSet.getObject( 2 );
System.out.println( "ResultSetTest.testResultSet " +name);
}
//关闭结果集
if (resultSet != null) {
resultSet.close();
}
------------
execute:
/**
* execute方法用来执行sql,查询类型select和修改类型update delete insert都可以执行
* 返回类型:
* 1>true: 表示当前执行的是select方法,在statement里面包含里结果集getResultSet
* 2>false 表示当前执行的是update,insert ,delete其中之一的方法,通过getUpdateCount得到影响的结果整型数值
*
*/
boolean execute = statement.execute( "delete from cat where name='jerry'" );
if (execute) {
ResultSet resultSet = statement.getResultSet();
while (resultSet.next()) {
System.out.println( "resultSet.getObject(1) = " + resultSet.getObject( 1 ) );
System.out.println( "resultSet.getObject(2) = " + resultSet.getObject( 2 ) );
}
} else {
//表示执行的是update insert delete更新语句
int updateCount = statement.getUpdateCount();
System.out.println("执行的是更新语句,影响的行数为:"+updateCount);
}
--------------------
PreparedStatement:预编译的Statement,性能比较高,可以缓存经常执行的sql语句
用占位符的方式去传值,不用拼接字符串,避免了sql注入!
PreparedStatement preparedStatement = connection.prepareStatement( sql );
int index=1;
//给preparedStatement对象传参,从1开始传参,
//小技巧,如果有多个参数传入,并且以后还会添加参数,那么传参的索引一般用index++代替扩展性比较强
preparedStatement.setObject( index++, userName );
preparedStatement.setObject( index++, password );
ResultSet resultSet = preparedStatement.executeQuery();