学习来源 Java数据库开发与实战应用
主要是对java操作数据库的一些基本介绍,已经使用JUnit进行单元测试的一些基础知识
JDBC 基础
几大对象
DriverManager 驱动管理类
注册方式如下,使用反射技术
1
| Class.forName("com.mysql.jdbc.Driver");
|
如果使用如下代码注册会注册两次
1
| DriverManager.registerDriver(new Driver());
|
因为查看 com.mysql.jdbc.Driver
的源代码如下
1 2 3 4 5 6 7 8 9 10 11 12
| public class Driver extends NonRegisteringDriver implements java.sql.Driver { public Driver() throws SQLException { }
static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } } }
|
它已经自动注册了,所以只要在运行的时候把这个类加载进来就可以了, static块
会在类第一次加载进虚拟机的时候执行
用来获得 Connection
对象,获取连接的代码如下
1 2
| Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/imoocjdbctest", "root", "root");
|
有三个参数,第一个是数据库连接的url,第二个是用户名,第三个是密码
当然,推荐将这些配置写到 .properties
文件中,动态读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class JdbcUtil { private static final String driverClass; private static final String url; private static final String username; private static final String password;
static { Properties properties = new Properties(); InputStream inputStream = JdbcUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"); try { properties.load(inputStream); } catch (IOException e) { e.printStackTrace(); }
driverClass = properties.getProperty("driverClass"); url = properties.getProperty("url"); username = properties.getProperty("username"); password = properties.getProperty("password"); }
public static void loadClass() throws ClassNotFoundException { Class.forName(driverClass); }
public static Connection getConnection() throws Exception{ loadClass(); return DriverManager.getConnection(url ,username, password); } }
|
jdbc.properties
文件放在src目录(classpath)下就行了,文件内容如下
1 2 3 4
| driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/imoocjdbctest username=root password=root
|
Connection 连接对象
- 创建执行SQL语句对象
- 进行事务管理
setAutoCommit(boolean autoCommit)
是否自动提交事务
commit()
事务提交
rollback()
事务回滚
原则上 尽量晚创建,早释放
Statement 执行语句
- 分类:
Statement
普通的执行语句
PreparedStatement
预编译语句(推荐):防止sql注入,提高系统执行效率
CallableStatement
执行sql中的存储过程,继承自 PreparedStatement
第三个有点奇怪,数据库存储过程是什么东西,说白了就相当于是一个函数,比如说我有一个插入员工信息的mysql过程,要传入两个参数,传出一个参数,那么java代码里就这么写
1 2 3 4 5
| CallableStatement stmt = con.prepareCall("{call insertEmployee(?,?,?)}"); stmt.setInt(1, id); stmt.setString(2, name); stmt.registerOutParameter(3, java.sql.Types.VARCHAR); stmt.executeUpdate();
|
ResultSet 结果集
记得释放资源
释放资源的内容写在 finally
块中,最好封装成一个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public static void release(Statement stmt, Connection conn, ResultSet resultSet) { if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } resultSet = null; }
if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; }
if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } }
|
使用连接池
使用第三方连接池,可以有效地管理连接数据库的资源,提高系统信息效率,这里使用 c3p0
连接池,进行连接的方法如下
1 2 3 4 5 6 7
| private static final ComboPooledDataSource dataSource = new ComboPooledDataSource();
public static Connection getConnection() throws Exception{ Connection conn = dataSource.getConnection(); return conn; }
|
注意,需要使用静态单例单例,否则会出现 too many connections
的报错,具体分析见这篇文章:c3p0数据库连接池如何正确的关闭资源(“too many connections”的解决办法)s
和之前的1配置文件一样,将其放到classpath的根目录下,文件名是 c3p0-config.xml
1 2 3 4 5 6 7 8 9 10 11 12
| <?xml version="1.0" encoding="UTF-8"?> <c3p0-config>
<default-config> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/imoocjdbctest</property> <property name="user">root</property> <property name="password">root</property> <property name="initialPoolSize">5</property> <property name="maxPoolSize">20</property> </default-config> </c3p0-config>
|
而且获取连接之后也需要关闭的,调用 close()
方法不会关闭与数据库的 TCP 连接,而是将连接还回到池中去。虽然都是Connection对象,但归根到底它的 close()
方法只是一个接口,连接池有自己的实现,用单步调试就可以发现单纯用mysql驱动获取的 Connection.close()
和 用连接池获得的 Connection.close()
内容不一样,虽然我看不懂具体实现的原理…
单元测试
注解
标识注解
1 2 3 4 5 6 7 8 9
| @RunWith(Suite.class) @Suite.SuiteClasses({ Testcase2.class, Testcase1.class, Testcase3.class, }) public class TestAll { }
|
设置顺序
@Before
表示在所有方法运行前运行的方法;
@After
表示在所有的方法运行之后执行的方法;
@FixMethodOrder
指定类中测试方法运行的排序方式,有以下几个选项
@FixMethodOrder(MethodSorters.JVM)
根据文件中方法出现的先后顺序进行测试
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
根据方法名的拼写排序的顺序进行测试
@FixMethodOrder(MethodSorters.DEFAULT)
默认,可能会乱序
测试工具
@Test(expected = Exception.class)
预计会抛出异常
@Test(timeout = 1000)
预计运行时间在1000ms内,超出就算测试失败
assert...
断言,预期结果和实际结果进行比较
fail
主动失败