JDBC

1 什么是JDBC?

JDBC(Java DataBase Connectivity),即Java数据库连接!也就是说,Java程序员可以使用JDBC API来操作数据库。

最早JDBC是Java EE中的规范,但是现在已经添加到Java SE中了。也就是说,JDBC API在JDK中就已经存在了。与JDBC相关的包有:java.sql和javax.sql两个包!

2 JDBC原理

  早期SUN公司的天才们想编写一套可以连接天下所有数据库的API,但是当他们刚刚开始时就发现这是不可完成的任务,因为各个厂商的数据库服务器差异太大了。后来SUN开始与数据库厂商们讨论,最终得出的结论是,由SUN提供一套访问数据库的规范,并提供连接数据库的协议标准,然后各个数据库厂商会遵循SUN的规范提供一套访问自己公司的数据库服务器的API出现。SUN提供的范围命名为JDBC,而各个厂商提供的,遵循了JDBC规范的,可以访问自己数据库的API被称之为驱动!

现在大家已经知道了,想使用JDBC必须要有驱动才可以,驱动就是JDBC中接口的实现,并且是针对自己厂商的数据库服务器的实现。我们使用的是MySQL数据库服务器,那么就需要有MySQL驱动。

3.JDBC编码步骤

连接数据库需要MySQL的JDBC驱动,即:mysql-connector-java-5.1.13-bin.jar。

对数据库的操作可以大致分为两种:更新操作、查询操作。更新操作就是增、删、改,它没有结果,而查询操作是有结果的。

  1. A.       注册驱动
  2. B.        获取与数据库的连接
  3. C.       得到代表SQL语句的对象
  4. D.       发送SQL语句:DML、DQL
  5. E.        获取结果集
  6. F.        从结果集中获取数据
  7. G.       关闭

1、注册驱动

Driver d = new Driver();

DriverManager.registerDriver(d);

其中Driver是com.mysql.jdbc.Driver类型,这种方法使用了硬编码,也就是说将来如果想更换数据库是不可以的,因为代码中使用了MySQL提供的类。

其实JDBC为了让我们不出现硬编码,要求各个厂商提供的驱动都要可以把自己来注册到DriverManager中,答案在Driver()的源码中。

static {

try {

java.sql.DriverManager.registerDriver(new Driver());

} catch (SQLException E) {

throw new RuntimeException("Can't register driver!");

}

}

上面代码是com.mysql.jdbc.Driver类中的静态代码块,它会new一个自己类型的对象,然后再把自己注册到DriverManager中。我们都知道static块会在类被加载时就会执行,也就是说,在JVM加载Driver类时,已经把一个Driver类的对象注册到DriverManager中了,所以我们就不需要再去注册了。我们只需要保证Driver类被加载就OK了!修改上面加载驱动的代码如下:

Class.forName("com.mysql.jdbc.Driver");

上面代码是用来加载com.mysql.jdbc.Driver类的代码,这样就可以把Driver注册到DriverManager中了!

  在我们的代码中,不要出现驱动Jar包中的类!这方便我们将来切换数据库!!!

2、获取与数据库的连接

//方式一:

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day10", "root", "sorry");

//方式二:

Properties props = new Properties();

props.put("user", "root");//key看数据库的规定

props.put("password", "sorry");

props.put("useUnicode", "true");

props.put("characterEncoding", "utf8");

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day10", props);

//方式三:

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day10?user=root&password=sorry");

还可以使用DriverManager的registerDriver()方法,但这个方法通常我们是不会使用的,因为使用它会导致对特定驱动的依赖。

注意,数据库的URL需要查看相关文档,不同数据库不同版本,URL都不一定一样.

URL:   协议(jdbc):子协议(mysql):主机:端口/数据库

  我们知道在使用DriverManager的getConnection()方法时需要给出三个参数,其中一个是url,下面我们来聊一聊这个url是什么东西。JDBC使用的URL用于描述数据的来源,它的语法如下:

可以把它分为三个部分,每个部分中间都是冒号:

l  其中第一部分是jdbc,这是固定的;

l  第二部分为子协议名称,一般都是特定厂商的数据库名称,例如MySQL就是jdbc:mysql:…,Oracle就是jdbc:oracle:…;

l  第三部分由数据库厂商来确定,这一部分通常需要说明数据库服务器主机的IP、端口,以及数据库名称。

MySQL的url:jdbc:mysql://localhost:3306/mydb1

其实还可以给url添加参数,最为常见的参数就是:

l  useUnicode=true

l  characterEncoding=UTF8

这两个参数是指定获取的Connection使用的编码!当然如果没有指定,那么会使用当前MySQL数据库的字符编码集。因为我们在安装MySQL时已经指定的UTF8,所以就算不指定这两个参数,获取到的连接也同样是UTF8的。

其实在不指定主机和端口时,默认也是连接localhost主机的3306端口,所以可以把url写成如下的样子:

jdbc:mysql:///mydb1

当然,最后的mydb1是要连接的数据库,这部分是不能少的。

建议把url写完整了:

jdbc:mysql://localhost:3306/mydb1?useUnicode=true&characterEncoding=UTF8

3、得到代表SQL语句的对象

Statement stmt = conn.createStatement();

4、发送SQL语句:DML、DQL

ResultSet rs = stmt.executeQuery("selcet chinese,english,math from student");

//Statement常用方法

//ResultSet executeQuery(String sql):sql一般是DQL语句, 用来发送查询语句.

//int executeUpdate(String sql):sql一般是没有返回结果集的语句,比如DML、DDL。返回值是影响到的行数, 用来发送增、删、改语句

//boolean execute(String sql):sql可以任何的语句。返回值:如果执行的sql语句有结果集,返回true,没有结果集,返回false

5、如果是DQL语句,有结果,得到返回的结果

6、遍历结果集

while(rs.next()){

System.out.println("---------------------------");

System.out.print(rs.getObject("chinese")+"\t");

System.out.print(rs.getObject("english")+"\t");

System.out.println(rs.getObject("math"));

}

7、释放占用的资源(官方文档,最好写成工具类.)

前面我们在获取Connection时使用了硬编码,把driverClassName、url、username、password都直接写到了Java代码中,如果将来需要更换数据库,或者更换用户来登录数据库,这都不方便,所以我们应该把连接数据库的数据写到配置文件中,例如:

//JDBC工具类

public class JdbcUtil {

private static String driverClass;

private static String url;

private static String user;

private static String password;

static{

try {

//从配置文件中读取信息

InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("dbcfg.properties");

Properties pro = new Properties();

pro.load(in);

driverClass = pro.getProperty("driverClass");

url = pro.getProperty("url");

user = pro.getProperty("user");

password = pro.getProperty("password");

Class.forName(driverClass);

catch (Exception e) {

throw new ExceptionInInitializerError(e);

}

}

public static Connection getConnection() throws Exception {

return DriverManager.getConnection(url,user,password);

}

public static void release(ResultSet rs,Statement stat,Connection conn){

if(rs!=null){

try {

rs.close();

catch (SQLException e) {

e.printStackTrace();

}

rs = null;

}

if(stat!=null){

try {

stat.close();

catch (SQLException e) {

e.printStackTrace();

}

stat = null;

}

if(conn!=null){

try {

conn.close();

catch (SQLException e) {

e.printStackTrace();

}

conn = null;

}

}

}

3.SQL攻击

在需要用户输入的地方,用户输入的是SQL语句的片段,最终用户输入的SQL片段与我们DAO中写的SQL语句合成一个完整的SQL语句!例如用户在登录时输入的用户名和密码都是为SQL语句的片段!

演示SQL攻击

首先我们需要创建一张用户表,用来存储用户的信息。

CREATE TABLE tab_user(

       uid  CHAR(32) PRIMARY KEY,

       username       VARCHAR(30) UNIQUE KEY NOT NULL,

       PASSWORD        VARCHAR(30)

);

 

INSERT INTO tab_user VALUES('U_1001', 'zs', 'zs');

SELECT * FROM tab_user;

现在用户表中只有一行记录,就是zs。

下面我们写一个login()方法!

public void login(String username,   String password) {

Connection con = null;

Statement stmt = null;

ResultSet rs = null;

try {

con = JdbcUtils.getConnection();

stmt = con.createStatement();

String sql = "SELECT * FROM tab_user WHERE " +

"username='" + username +

"' and password='" +   password + "'";

rs = stmt.executeQuery(sql);

if(rs.next()) {

System.out.println("欢迎" + rs.getString("username"));

} else {

System.out.println("用户名或密码错误!");

}

} catch (Exception e) {

throw new RuntimeException(e);

} finally {

JdbcUtils.close(con, stmt,   rs);

}

}

下面是调用这个方法的代码:

login("a' or 'a'='a", "a' or 'a'='a");

这行当前会使我们登录成功!因为是输入的用户名和密码是SQL语句片段,最终与我们的login()方法中的SQL语句组合在一起!我们来看看组合在一起的SQL语句:

SELECT * FROM tab_user WHERE username='a' or 'a'='a' and password='a' or 'a'='a'

2 防止SQL攻击

过滤用户输入的数据中是否包含非法字符;

分步交验!先使用用户名来查询用户,如果查找到了,再比较密码;

使用PreparedStatement

PreparedStatement是Statement的子接口,你可以使用PreparedStatement来替换Statement。

PreparedStatement的好处:

防止SQL攻击;

提高代码的可读性,以可维护性;

提高效率。(支持预编译SQL –占位符’? ’)

2 PreparedStatement的使用

String sql = “select * from   tab_student where s_number=?”;

PreparedStatement pstmt = con.prepareStatement(sql);

pstmt.setString(1, “S_1001”);

ResultSet rs = pstmt.executeQuery();

rs.close();

pstmt.clearParameters();//再次使用时需要把原来的设置清空。

pstmt.setString(1, “S_1002”);

rs = pstmt.executeQuery();

在使用Connection创建PreparedStatement对象时需要给出一个SQL模板,所谓SQL模板就是有“?”的SQL语句,其中“?”就是参数。

在得到PreparedStatement对象后,调用它的setXXX()方法为“?”赋值,这样就可以得到把模板变成一条完整的SQL语句,然后再调用PreparedStatement对象的executeQuery()方法获取ResultSet对象。

注意PreparedStatement对象独有的executeQuery()方法是没有参数的,而Statement的executeQuery()是需要参数(SQL语句)的。因为在创建PreparedStatement对象时已经让它与一条SQL模板绑定在一起了,所以在调用它的executeQuery()和executeUpdate()方法时就不再需要参数了。

PreparedStatement最大的好处就是在于重复使用同一模板,给予其不同的参数来重复的使用它。这才是真正提高效率的原因。

所以,建议大家在今后的开发中,无论什么情况,都去需要PreparedStatement,而不是使用Statement

4.DAO解耦

把控制权转移到外面,想要创建对象,必须先传参数依赖注入

public void setDao(User dao){

this.dao = dao;

{

另一种方式通过构造函数转过来

单开就是  单例设计模式

//到底使用哪一个,是由自己指定的。是new出来的,没法解耦

//如果由外部传入使用的实现类,这个过程称之为控制反转,就可以解耦了.IoC DI:依赖注入:Spring的核心

//工厂创建模式,就是把创建的细节隐藏起来.

private UserDao dao = DaoFactory.getInstance().getUserDaoImpl();// = new UserDaoMySQLEnhanceImpl();

//    public UserServiceImpl(UserDao dao){//依赖注入方法一,通过构造方式

//        this.dao = dao;

//    }

//    public void setDao(UserDao dao){//依赖注入方式二,通过set方法

//        this.dao = dao;

//    }

//创建DAO实例的工厂:饿汉子式单例

public class DaoFactory {

//先创建工厂类,需要私有化,为了能一家在就实例化,需要静态修饰

private static DaoFactory instance = new DaoFactory();

//无参构造函数私有化

private DaoFactory(){}

//定义静态方法,调用类

public static DaoFactory getInstance(){

return instance;

}

//私有Properties类,

private static Properties props = new Properties();

static{

//静态代码块,初始化就加载,这样就参数就可以使用字符串,就可以写到配置文件中.

InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("dao.properties");

try {

props.load(in);

catch (IOException e) {

throw new RuntimeException(e);

}

}

//创建一个UserDaoImpl实例类,需要的参数读取配置文件

//这样就进行了解耦,所有需要的参数可以通过修改配置文件来完成

public UserDao getUserDaoImpl(){

try {

String daoImplName = props.getProperty("userDao");

return (UserDao)Class.forName(daoImplName).newInstance();

catch (Exception e) {

throw new RuntimeException(e);

}

}

}

5 小知识点

读取二进制文件

我们一直在向数据库保存varhcar、int、date等类型的数据,现在我们要尝试把一张图片或一个mp3保存到数据库中去。

首先我们需要创建一张表,表中要有一个mediumblob(16M)类型的字段。

CREATE TABLE tab_bin(

       id   INT       PRIMARY   KEY AUTO_INCREMENT,

       filename VARCHAR(100),

       data       MEDIUMBLOB

);

  向数据库插入二进制数据需要使用PreparedStatement为原setBinaryStream(int, InputSteam)方法来完成。

con = JdbcUtils.getConnection();

String sql = "insert into tab_bin(filename,data) values(?,   ?)";

pstmt = con.prepareStatement(sql);

pstmt.setString(1, "a.jpg");

InputStream in = new FileInputStream("f:\\a.jpg");//得到一个输入流对象

pstmt.setBinaryStream(2, in);// 为第二个参数赋值为流对象

pstmt.executeUpdate();

读取二进制数据,需要在查询后使用ResultSet类的getBinaryStream()方法来获取输入流对象。也就是说,PreparedStatement有setXXX(),那么ResultSet就有getXXX()。

con = JdbcUtils.getConnection();

String sql = "select filename,data from tab_bin where   id=?";

pstmt = con.prepareStatement(sql);

pstmt.setInt(1, 1);

rs = pstmt.executeQuery();

rs.next();

String filename = rs.getString("filename");

OutputStream out = new   FileOutputStream("F:\\" + filename);

//使用文件名来创建输出流对象。

InputStream in =   rs.getBinaryStream("data");//读取输入流对象

IOUtils.copy(in, out);// 把in中的数据写入到out中。

out.close();

  还有一种方法,就是把要存储的数据包装成Blob类型,然后调用PreparedStatement的setBlob()方法来设置数据

con = JdbcUtils.getConnection();

String sql = "insert into tab_bin(filename,data) values(?, ?)";

pstmt = con.prepareStatement(sql);

pstmt.setString(1, "a.jpg");

File file = new File("f:\\a.jpg");

byte[] datas = FileUtils.getBytes(file);//获取文件中的数据

Blob blob = new SerialBlob(datas);//创建Blob对象

pstmt.setBlob(2, blob);//设置Blob类型的参数

pstmt.executeUpdate();

con = JdbcUtils.getConnection();

String sql = "select filename,data from tab_bin where id=?";

pstmt = con.prepareStatement(sql);

pstmt.setInt(1, 1);

rs = pstmt.executeQuery();

rs.next();

String filename = rs.getString("filename");

File file = new File("F:\\" + filename) ;

Blob blob = rs.getBlob("data");

byte[] datas = blob.getBytes(0, (int)file.length());

FileUtils.writeByteArrayToFile(file,   datas);

批处理SQL

语句不同,有很多行,可以使用addBatch方法批处理,别忘了executeBatch一下.

其实Batch里面就是封装了一个缓存

//向t1表中插入两条记录,删除第一条记录

//利用statement可以执行SQL语句不同的批处理

public void testBatch1(){

Connection conn = null;

Statement stmt = null;

try{

conn = JdbcUtil.getConnection();

stmt = conn.createStatement();

//把要输入的SQL语句放入字符串变量中去;

String sql1 = "insert into t1 (id,name) values(1,'aaa1')";

String sql2 = "insert into t1 (id,name) values(2,'bbb1')";

String sql3 = "delete from t1 where id=1";

//注入SQL语句,是注入,并没有执行;

stmt.addBatch(sql1);

stmt.addBatch(sql2);

stmt.addBatch(sql3);

//对注入的SQL语句执行

//数组的元素表示每条语句影响到的行数。

int[] i = stmt.executeBatch();

}catch(Exception e){

throw new RuntimeException(e);

}finally{

JdbcUtil.release(null, stmt, conn);

}

}

利用PreparedStatement执行批处理,只能用在SQL语句相同的情况下。参数有可能不同

//向t1表中批量插入10条记录,语句相同,参数不同

@Test

public void testBatch2(){

Connection conn = null;

PreparedStatement stmt = null;

try{

conn = JdbcUtil.getConnection();

//一开始就需要把SQL语句加载到PreparedStatement中,用占位符

String sql="insert into t1 (id,name) values(?,?)";

stmt = conn.prepareStatement(sql);

//输入SQL语句中的参数

for(int x=0;x<10;x++){

stmt.setInt(1, x+1);

stmt.setString(2, "aaa"+(x+1));

//对参数进行注入

stmt.addBatch();

}

//对注入的参数执行

stmt.executeBatch();

}catch(Exception e){

throw new RuntimeException(e);

}finally{

JdbcUtil.release(null, stmt, conn);

}

}

OCI  效率高  但是要装客户文件

Thin 效率低 但是不需要装

一般开发用这个Thin

插入1000001条记录,大数据插入

//向t1表中批量插入1000001条记录

@Test

public void testBatch3(){

Connection conn = null;

PreparedStatement stmt = null;

try{

conn = JdbcUtil.getConnection();

//一开始就需要把SQL语句加载到PreparedStatement中,用占位符

String sql="insert into t1 (id,name) values(?,?)";

stmt = conn.prepareStatement(sql);

//输入SQL语句中的参数

for(int x=0;x<1000001;x++){

stmt.setInt(1, x+1);

stmt.setString(2, "aaa"+(x+1));

stmt.addBatch();

//定义一个判断,每插入1000条就执行一次,还需要清空注入的语句.

if(x%1000==0){

stmt.executeBatch();//执行

stmt.clearBatch();//清空

}

}

//扫尾执行,因为可能会有尾数

stmt.executeBatch();

}catch(Exception e){

throw new RuntimeException(e);

}finally{

JdbcUtil.release(null, stmt, conn);

}

}

返回自增长主键

当表的主键是自增长的,那么就是由数据库来维护主键的值。当我们使用Java向数据库插入一行记录后,主键的值我们是不知道的。当然你可以去再查询一次!但是这不是最好的办法。

想获取主键自增长的值,需要在创建PreparedStatement时开始:

String sql = "insert into tt2(name) values(?)";

pstmt = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

//表示在执行SQL语句后会去获取主键自增长的值。

pstmt.setString(1,   "hello");

pstmt.executeUpdate();

在执行完INSERT语句之后,可以通过PrepareStatement的getGeneratedKeys()方法获取一个结果集对象,然后再获取结果集中唯一一行,唯一一列的数据,这就是主键自增长的值。

ResultSet rs =   pstmt.getGeneratedKeys();//获取自增长主键结果集

rs.next();

int id = rs.getInt(1);

System.out.println(id);

调用存储过程

1 创建存储过程

首先创建3个存在过程:

l  无参的;

l  入参的;

l  出参的。

DELIMITER //

CREATE PROCEDURE pro1()

BEGIN

SELECT   * FROM stu;

END//

DELIMITER ;

DELIMITER //

CREATE PROCEDURE pro2(IN _sid   VARCHAR(10))

BEGIN

SELECT * FROM stu WHERE sid=_sid;

END//

DELIMITER ;

DELIMITER //

CREATE PROCEDURE pro3(IN _sid   VARCHAR(10), OUT _sname VARCHAR(20))

BEGIN

SELECT sname INTO _sname FROM stu WHERE sid=_sid;

END//

DELIMITER ;

2 调用pro1(无参存储过程)

要调用存储过程需要使用CallableStatement,获取CallableStatement需要调用Connection的prepareCall(String sql)方法。

con = JdbcUtils.getConnection();

String sql = "{call pro1()}";//调用存储过程的SQL语句需要一对大括号括起来

cstmt = con.prepareCall(sql);

rs = cstmt.executeQuery();//如果存储过程会返回结果集,那么就调用executeQuery()方法,否则调用executeUpdate()方法。

while(rs.next()) {

int colCnt =   rs.getMetaData().getColumnCount();

for(int i = 1; i <= colCnt; i++) {

Object o = rs.getObject(i);

System.out.print(o + " ");

}

System.out.println();

}

3 调用有入参存储过程

con = JdbcUtils.getConnection();

String sql = "{call pro2(?)}";//给出一个参数

cstmt = con.prepareCall(sql);

cstmt.setString(1, "S_100");//为参数赋值

rs = cstmt.executeQuery();

rs.next();

System.out.println(rs.getString(1) + ", " + rs.getString(2)

+   ", " + rs.getInt(3) + ", " + rs.getString(4));

4 调用有出参的存储过程

con = JdbcUtils.getConnection();

String sql = "{call   pro3(?,?)}";//第一个参数是入参,第二个是出参。

cstmt = con.prepareCall(sql);

cstmt.setString(1, "S_100");

cstmt.registerOutParameter(2, Types.VARCHAR);// 注册一个出参!说明它的SQL类型

cstmt.execute();//这里调用executeUpdate()、executeQuery()或是execute()都行!

String name = cstmt.getString(2);// 获取出参的值

System.out.println(name);

元数据

1 DatabaseMetaData

获取DatabaseMetaData对象:

Connection con =   ...

DatabaseMetaData   dbmd = con.getMetaData();

基本功能

String name =   dbmd.getDatabaseProductName();//获取数据库名称:MySQL

int v1 = dbmd.getDatabaseMajorVersion();//获取数据库主版本号:5

int v2 = dbmd.getDatabaseMinorVersion();//获取数据库次版本号:1

System.out.println(name + v1 + "." + v2);

String driverName   = dbmd.getDriverName();//获取驱动名

String   driverVersion = dbmd.getDriverVersion();//获取驱动版本

System.out.println(driverName   + ", " + driverVersion);

String url =   dbmd.getURL();//获取URL

String username =   dbmd.getUserName();//获取用户名

System.out.println(url);

System.out.println(username);

获取所有数据库名称

ResultSet rs =   dbmd.getCatalogs();

while(rs.next()) {

System.out.println(rs.getString(1));

}

获取指定数据库中所有表名称

ResultSet rs =   dbmd.getTables("mydb1", null, null, new String[]{"TABLE"});

while(rs.next())      {

System.out.println(rs.getString("TABLE_NAME"));

}

2 ParameterMetaData

得到ParameterMataData对象

Connection con = ...

String sql = "insert into tab_student value(?,?,?,?)";

PreparedStatement   pstmt = con.prepareStatement(sql);

ParameterMetaData pmd =   pstmt.getParameterMetaData();

ParameterMetaData是针对“?”的元数据!但是很多驱动对它的支持不是很好。

int cnt = pmd.getParameterCount();//参数的个数

for(int i = 1; i <= cnt; i++) {

System.out.println(pmd.getParameterTypeName(i));//当前参数类型名

System.out.println(pmd.getParameterType(i));//当前参数类型

System.out.println(pmd.getParameterClassName(i));//当前参数Java类型名

System.out.println(pmd.isNullable(i));//当前参数是否可以为NULL

}

3 结果集列数据:ResultSetMetaDate

获取ResultSetMetaData

ResultSet rs = ...;

ResultSetMetaData rsmd = rs.getMetaData();

方法介绍:

ResultSetMetaData rsmd =   rs.getMetaData();

int cnt = rsmd.getColumnCount();//获取结果集列数

for(int i = 1; i <= cnt; i++) {

System.out.print(rsmd.getColumnName(i));//获取当前列名称

if(i < cnt) {

System.out.print(", ");

}

}

System.out.println();

for(int i = 1; i <= cnt; i++) {

System.out.print(rsmd.getColumnClassName(i));//获取当前列Java类型名

if(i < cnt) {

System.out.print(", ");

}

}

System.out.println();

while(rs.next()) {

for(int i = 1; i <= cnt; i++) {

System.out.print(rs.getObject(i));//看清楚,这个是rs不是rsmd

if(i < cnt) {

System.out.print(", ");

}

}

System.out.println();

}

JDBC二部曲之_入门的更多相关文章

  1. JDBC二部曲之_事物、连接池

    事务 事务概述 事务的四大特性(ACID) 事务的四大特性是: l  原子性(Atomicity):事务中所有操作是不可再分割的原子单位.事务中所有操作要么全部执行成功,要么全部执行失败. l  一致 ...

  2. c语言项目开发流程二部曲

    一.在第一部曲中我们介绍了电子词典项目开发的前5步,下面继续我们的步伐. 6.函数接口设计,这一步不是一蹴而就的,在项目进行中得不断修改,下面是我电子词典项目接口. /**************函数 ...

  3. Struts2.3.34+Hibernate 4.x+Spring4.x 整合二部曲之上部曲

    1 导入jar包 可以复制jar包或maven导入,本文最后会给出github地址 2 导入log4j.properties文件 og4j.appender.stdout=org.apache.log ...

  4. Struts2.3.34+Hibernate 4.x+Spring4.x 整合二部曲之下部曲

    1 导入jar包 本文最后会给出项目的地址,各位无须看急. 2 配置web.xml文件 <?xml version="1.0" encoding="UTF-8&qu ...

  5. JDBC编程六部曲

    今天初学jdbc,明白了大致的编程流程,在此总结一下: JDBC编程可以分为六步——六部曲: * 第一步:注册驱动. * 1.1 获取驱动对象 * 1.2 注册驱动 * 第二步:获取数据库连接 * 第 ...

  6. docker-compose下的java应用启动顺序两部曲之二:实战

    上篇回顾 本文是<docker-compose下的java应用启动顺序两部曲>的终篇,在上一篇<docker-compose下的java应用启动顺序两部曲之一:问题分析>中,我 ...

  7. 使用Java管理千台规模Linux服务器_入门

    http://www.oschina.net/code/snippet_222919_11734 代码分享 当前位置: 代码分享 » Java  » 网络编程 搜 索   [饶过] 使用Java管理千 ...

  8. (二)Hololens Unity 开发入门 之 Hello HoloLens~

    学习源于官方文档 微软官文~ 笔记一部分是直接翻译官方文档,部分各人理解不一致的和一些比较浅显的保留英文原文 (二)Hololens Unity 开发入门 之 Hello HoloLens~ 本文主要 ...

  9. Android系统--输入系统(十二)Dispatch线程_总体框架

    Android系统--输入系统(十二)Dispatch线程_总体框架 1. Dispatch线程框架 我们知道Dispatch线程是分发之意,那么便可以引入两个问题:1. 发什么;2. 发给谁.这两个 ...

随机推荐

  1. thinkPHP 表单自动验证功能

    昨天晚上我们老大叫我弄表单自动验证功能,愁了半天借鉴了好多官网的知识,才出来,诶,总之分享一下我自己的成果吧! thinkphp 在Model基类为我们定义了自动验证的函数和正则表达式,我们只需要在对 ...

  2. SpringMVC 集成 Freemarker 模板引擎

    本文通过 maven 项目中集成 1.引入 SpringMVC 与 Freemarker 需要的依赖 <!-- SpringMVC --> <dependency> <g ...

  3. win 7 查看端口被占用

    开始---->运行---->cmd,或者是window+R组合键,调出命令窗口     输入命令:netstat -ano,列出所有端口的情况.在列表中我们观察被占用的端口,比如是4915 ...

  4. 【转载】Unity插件研究院之自动保存场景

    原文: http://wiki.unity3d.com/index.php?title=AutoSave  最近发现Unity老有自动崩溃的BUG. 每次崩溃的时候由于项目没有保存所以Hierarch ...

  5. Python网络编程(OSI模型、网络协议、TCP)

    前言: 什么是网络? 网络是由节点和连线构成,表示诸多对象及其相互联系. 在数学上,网络是一种图,一般认为专指加权图. 网络除了数学定义外,还有具体的物理含义,即网络是从某种相同类 型的实际问题中抽象 ...

  6. Python全栈工程师(每周总结:2)

     ParisGabriel   感谢 大家的支持                                                               每天坚持 一天一篇 点个订 ...

  7. ES原理(转载)

    该博客属于转载,是很经典的一篇关于ES的介绍: Elasticsearch 是一个兼有搜索引擎和NoSQL数据库功能的开源系统,基于Java/Lucene构建,可以用于全文搜索,结构化搜索以及近实时分 ...

  8. CyclicBarrier和CountDownLatch的使用

    CyclicBarrier: api对CyclicBarrier的描述: 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).在涉及一组固定大 ...

  9. 201621123034 《Java程序设计》第6周学习总结

    作业06-接口.内部类 1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图或相关笔记,对面向对象思想进行一个总结. 注1:关键词与内容不求多 ...

  10. BI商业智能培训系列——(二)SSIS入门

    简介: SSIS,Microsoft SQL Server Integration Services.Integration意为"整合"."一体化".上篇博客中 ...