官方教程(包括 javase的基础部分):JDBC Basics

重新梳理、学习一下“Java连接数据库”相关的内容。

因为最开始没有认真学多线程和JDBC,一直在自己写的多线程程序中维持下面的错误写法:

  1. 多个线程共用一个connection
  2. connection只开不关

为什么上述做法是错误的呢? 可以参看这个帖子。- - “JDBC规范并未规定那三个对象必须是线程安全的,因此所有的JDBC厂商也不会去弄成线程安全的,正因为如此,所以就会有并发问题。” 、-- “ 并不是说不能把连接对象弄成成员变量,只是不能将其弄成成员变量后,在多线程环境下处于共享这些对象,如果同步处理得不好,那就会产生严重的连接泄漏。为了避免这种情况发生,仅在用时获取连接,用完后马上关掉。” -- “如果你对JDBC、多线程编程没有达到非常熟练的程度,还是老老实实地使用经典的JDBC代码结构。” -- 摘抄自csdn 火龙果被占用了

另外,connection只开不关很容易导致连接失效(mysql默认保持连接的时间是8小时,如果这个连接在8小时内无人访问的话,就会关闭这个连接。- -摘)

我把这些错误代码放在第一小节记录下来,作为java连接数据库的最原始版本,在这之后逐渐改良成可以适应各种场景的正确代码。

① DDL.sql

DROP TABLE IF EXISTS `profile`;
CREATE TABLE `profile` (
`profileId` BIGINT(20) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL,
`password` VARCHAR(50) NOT NULL,
`nickname` VARCHAR(50) NOT NULL,
`last_online` TIMESTAMP NULL DEFAULT NULL,
`gender` CHAR(1) NULL DEFAULT NULL,
`birthday` TIMESTAMP NULL DEFAULT NULL,
`location` VARCHAR(50) NULL DEFAULT NULL,
`joined` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`profileId`),
UNIQUE INDEX `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8;

profile表可以基本等价为user表来理解。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

② 对应的实体类Profile.java

package org.sample.entity;

import java.sql.Timestamp;

public class Profile {
private Long profileId;
private String username;
private String password;
private String nickname;
private Timestamp last_online;
private Character gender;
private Timestamp birthday;
private String location;
private Timestamp joined; public Profile() {
} public Long getProfileId() {
return profileId;
} public void setProfileId(Long profileId) {
this.profileId = profileId;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public String getNickname() {
return nickname;
} public void setNickname(String nickname) {
this.nickname = nickname;
} public Timestamp getLast_online() {
return last_online;
} public void setLast_online(Timestamp last_online) {
this.last_online = last_online;
} public Character getGender() {
return gender;
} public void setGender(Character gender) {
this.gender = gender;
} public Timestamp getBirthday() {
return birthday;
} public void setBirthday(Timestamp birthday) {
this.birthday = birthday;
} public String getLocation() {
return location;
} public void setLocation(String location) {
this.location = location;
} public Timestamp getJoined() {
return joined;
} public void setJoined(Timestamp joined) {
this.joined = joined;
} @Override
public String toString() {
return "Profile{" +
"profileId=" + profileId +
", username='" + username + '\'' +
", password='" + password + '\'' +
", nickname='" + nickname + '\'' +
", last_online=" + last_online +
", gender=" + gender +
", birthday=" + birthday +
", location='" + location + '\'' +
", joined=" + joined +
'}';
}
}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

③ ConnectionFactory.java或者常说的Dbutil(错误代码 ↓

package org.sample.db;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ResourceBundle; /**
* 单线程适用,只开不关,反复用一个 Connection
*/
public class StaticConnectionFactory { private static ResourceBundle rb = ResourceBundle.getBundle("org.sample.db.db-config"); private static final String JDBC_URL = rb.getString("jdbc.url"); private static final String JDBC_USER = rb.getString("jdbc.username"); private static final String JDBC_PASSWORD = rb.getString("jdbc.password"); private static Connection conn = null; static {
try {
// Class.forName("org.gjt.mm.mysql.Driver");
// JDBC 4.0 之后(包括 JDBC 4.0)不再需要 class.forName ,详细查看 javaSE6 之后的 API
conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
} catch (SQLException e) {
throw new RuntimeException("Error connecting to the database", e);
}
} public static Connection getConnection() {
return conn;
} public static void setAutoCommit(boolean autoCommit) throws SQLException {
conn.setAutoCommit(autoCommit);
} public static void commit() throws SQLException {
conn.commit();
} public static void rollback() throws SQLException {
conn.rollback();
}
}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

④ org/sample/db/db-config.properties

jdbc.url=jdbc:mysql://***.**.**.**:3306/profiles?characterEncoding=utf8
jdbc.username=root
jdbc.password=aaaaaaaaaaa

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

⑤ ProfileDAO.java

package org.sample.dao;

import org.sample.entity.Profile;

import java.util.List;

public interface ProfileDAO {

    int saveProfile(Profile profile);

    List<Profile> listProfileByNickname(String nickname);

    Profile getProfileByUsername(String username);

    int updateProfileById(Profile profile);

    int updatePassword(String username, String password);

    int updateLastOnline(String username);
}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

⑥ ProfileDAOImpl.java(为了用“带资源的try”严重画蛇添足了。)

package org.sample.dao.impl;

import com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException;
import org.sample.dao.ProfileDAO;
import org.sample.db.StaticConnectionFactory;
import org.sample.entity.Profile; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List; // NotThreadSafe
public class ProfileDAOImpl implements ProfileDAO { private static final Connection conn = StaticConnectionFactory.getConnection(); @Override
public int saveProfile(Profile profile) {
int i = 0;
try (
PreparedStatement ps =
createPreparedStatementForSaveProfile(conn, profile);
) {
i = ps.executeUpdate();
} catch (SQLException e) {
if (!(e instanceof MySQLIntegrityConstraintViolationException)) {
e.printStackTrace();
}
}
return i;
} @Override
public List<Profile> listProfileByNickname(String nickname) {
List<Profile> profiles = new ArrayList<>();
try (
PreparedStatement ps =
createPreparedStatementForListProfileByNickname(conn, nickname);
ResultSet rs = ps.executeQuery();
) {
while (rs.next()) {
Profile profile = extractProfileFromResultSet(rs);
profiles.add(profile);
}
} catch (SQLException e) {
e.printStackTrace();
}
return profiles;
} @Override
public Profile getProfileByUsername(String username) {
Profile profile = null;
try (
PreparedStatement ps =
createPreparedStatementForGetProfileByUsername(conn, username);
ResultSet rs = ps.executeQuery();
) {
if (rs.next()) {
profile = extractProfileFromResultSet(rs);
}
} catch (SQLException e) {
e.printStackTrace();
}
return profile;
} @Override
public int updateProfileById(Profile profile) {
int i = 0;
try (
PreparedStatement ps =
createPreparedStatementForUpdateProfileById(conn, profile);
) {
i = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return i;
} @Override
public int updatePassword(String username, String password) {
int i = 0;
try (
PreparedStatement ps =
createPreparedStatementForUpdatePassword(username, password);
) {
i = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return i;
} @Override
public int updateLastOnline(String username) {
int i = 0;
try (
PreparedStatement ps =
createPreparedStatementForUpdateLastOnline(username);
) {
i = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return i;
} private Profile extractProfileFromResultSet(ResultSet rs) throws SQLException {
Profile profile = new Profile();
profile.setBirthday(rs.getTimestamp("birthday"));
profile.setJoined(rs.getTimestamp("joined"));
profile.setLast_online(rs.getTimestamp("last_online"));
profile.setLocation(rs.getString("location"));
profile.setNickname(rs.getString("nickname"));
profile.setPassword(rs.getString("password"));
profile.setProfileId(rs.getLong("profile_id"));
profile.setUsername(rs.getString("username"));
if (rs.getString("gender") != null) {
profile.setGender(rs.getString("gender").charAt(0));
}
return profile;
} private PreparedStatement createPreparedStatementForSaveProfile(Connection conn, Profile profile) throws SQLException {
String sql = "INSERT INTO `profiles`.`profile` (`username`, `password`, `nickname`) " +
"VALUES (?, ?, ?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, profile.getUsername());
ps.setString(2, profile.getPassword());
ps.setString(3, profile.getNickname());
return ps;
} private PreparedStatement createPreparedStatementForListProfileByNickname(Connection conn, String nickname) throws SQLException {
String sql = "SELECT `profile_id`, `username`, `password`, `nickname`, `last_online`, `gender`, `birthday`, `location`, `joined`" +
"FROM `profiles`.`profile`" +
"WHERE `nickname`=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, nickname);
return ps;
} private PreparedStatement createPreparedStatementForGetProfileByUsername(Connection conn, String username) throws SQLException {
String sql = "SELECT `profile_id`, `username`, `password`, `nickname`, `last_online`, `gender`, `birthday`, `location`, `joined`" +
"FROM `profiles`.`profile`" +
"WHERE `username`=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, username);
return ps;
} private PreparedStatement createPreparedStatementForUpdateProfileById(Connection conn, Profile profile) throws SQLException {
String sql = "UPDATE `profiles`.`profile`" +
"SET `nickname`=?, `gender`=?, `birthday`=?, `location`=? " +
"WHERE `profile_id`=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, profile.getNickname());
ps.setString(2, profile.getGender() != null ? String.valueOf(profile.getGender()) : null);
ps.setTimestamp(3, profile.getBirthday());
ps.setString(4, profile.getLocation());
ps.setLong(5, profile.getProfileId());
return ps;
} private PreparedStatement createPreparedStatementForUpdatePassword(String username, String password) throws SQLException {
String sql = "UPDATE `profiles`.`profile`" +
"SET `password`=? " +
"WHERE `username`=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, password);
ps.setString(2, username);
return ps;
} private PreparedStatement createPreparedStatementForUpdateLastOnline(String username) throws SQLException {
String sql = "UPDATE `profiles`.`profile`" +
"SET `last_online`=CURRENT_TIMESTAMP " +
"WHERE `username`=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, username);
return ps;
}
}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

⑦ ProfileDAOTest.java

package org.sample.dao;

import org.junit.Test;
import org.sample.dao.impl.ProfileDAOImpl;
import org.sample.db.StaticConnectionFactory;
import org.sample.entity.Profile; import java.sql.SQLException;
import java.util.List; import static org.junit.Assert.*; public class ProfileDAOTest { private ProfileDAO profileDAO = new ProfileDAOImpl(); private static final String USER_NAME = "hello123"; @Test
public void saveProfile() {
Profile profile = new Profile();
profile.setUsername(USER_NAME);
profile.setPassword("231231232");
profile.setNickname("jack");
int i = profileDAO.saveProfile(profile);
System.out.println(i);
} @Test
public void listProfileByNickname() {
List<Profile> profiles = profileDAO.listProfileByNickname("123");
} @Test
public void getProfileByUsername() {
Profile existProfile = profileDAO.getProfileByUsername(USER_NAME);
Profile notExistProfile = profileDAO.getProfileByUsername(USER_NAME + "321");
assertNotNull(existProfile);
assertNull(notExistProfile);
} @Test
public void updateProfileById() {
Profile profile = profileDAO.getProfileByUsername(USER_NAME);
int i = profileDAO.updateProfileById(profile);
assertEquals(1, i); // 即便没改变值,但是还是会重新set一遍,因此影响行数还是一行 profile.setGender('f');
profile.setNickname("www" + Math.random());
int j = profileDAO.updateProfileById(profile);
assertEquals(1, j);
} @Test
public void updatePassword() {
profileDAO.updatePassword(USER_NAME, "www" + Math.random());
} @Test
public void updateLastOnline() throws SQLException {
try {
StaticConnectionFactory.setAutoCommit(false);
profileDAO.getProfileByUsername(USER_NAME);
profileDAO.updateLastOnline(USER_NAME);
StaticConnectionFactory.commit();
} catch (SQLException e) {
e.printStackTrace();
try {
StaticConnectionFactory.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
StaticConnectionFactory.setAutoCommit(true);
}
}
}

Java连接数据库 #01# JDBC单线程适用的更多相关文章

  1. Java连接数据库 #02# JDBC经典套路

    内容索引 LocalConnectionFactory.java LocalConnectionProxy.java ProfileDAO.java-2.0 ProfileDAOImpl.java-2 ...

  2. java连接数据库(jdbc)的标准规范

    java连接数据库的标准规范 JDBC全称:java database connectivity ,是sun公司提供的Java连接数据库的标准规范. localhost和127.0.0.1 都是表示当 ...

  3. 学数据库你竟然不用用JAVA写代码,可惜你遇到了我! JAVA连接数据库(JDBC)的安装使用教程

    Step 1 你得有Eclipse 没有出门右拐,我教不了你. Step 2 你得有Mysql MySQL的详细安装过程,我在另一篇博客中给出.戳我 Step 3 安装JDBC 可以去官网下,如果用的 ...

  4. 从零开始学JAVA(04)-连接数据库MSSQL(JDBC准备篇)

    在JAVA中可以使用JDBC连接数据库,不管是哪种数据库,首先必须下载驱动,包括Windows的MSSQL. 1.下载MSSQL的JDBC驱动,可以通过百度“Microsoft JDBC Driver ...

  5. 完整java开发中JDBC连接数据库代码和步骤[申明:来源于网络]

    完整java开发中JDBC连接数据库代码和步骤[申明:来源于网络] 地址:http://blog.csdn.net/qq_35101189/article/details/53729720?ref=m ...

  6. java连接数据库(jdbc)调用配置文件

    各种语言都有自己所支持的配置文件,后缀名“.properties”结尾的就是其中之一. 在java连接数据库时,采取读取配置文件的方式,来获取数据库连接. 新建jdbc.properties文件,内容 ...

  7. Java数据库连接技术——JDBC

    大家好,今天我们学习了Java如何连接数据库.之前学过.net语言的数据库操作,感觉就是一通百通,大同小异. JDBC是Java数据库连接技术的简称,提供连接各种常用数据库的能力. JDBC API ...

  8. Java连接数据库的辣几句话

    Java连接数据库的辣几句话 1.java连接Oracle数据库 使用以下代码三个步骤: 1.下载ojdbc.jar包并导入项目中.附下载地址:http://download.csdn.net/det ...

  9. servlet中Java连接数据库后的基本操作

    servlet中Java连接数据库后的基本操作 在eclipse中新建一个工程:login 在Server中新建一个服务器,基本的操作不用说了,在前两天的笔记中可以找到; 需要知道数据库的用户名和密码 ...

随机推荐

  1. xtrabackup全库还原+binlog日志还原

    1.场景 mysql数据库误删某个库.误删表或者误删除数据 如下模拟图:备份策略定为每天凌晨进行全库备份,在B时间点进行了误操作以后,有两种恢复场景,一种是恢复到B时间点误操作前,一种是恢复到C时间点 ...

  2. Golang--Hello World

    //1)go语言以包作为管理单位 //2)每个文件必须先声明包 //3)程序必须有一个main包 package main import "fmt" //入口函数 func mai ...

  3. Spring Boot下的lombok安装以及使用简介

    引言:lombok是一套代码模板解决方案,将极大提升开发的效率,这里介绍给大家使用. 1. Lombok lombok是一个可以通过简单的注解的形式来帮助我们简化消除一些必须有但显得很臃肿的 Java ...

  4. 【LeetCode每天一题】Longest Valid Parentheses(最长有效括弧)

    Given a string containing just the characters '(' and ')', find the length of the longest valid (wel ...

  5. char varchar

    对于字符类型的有:char:固定长度,存储ANSI字符,不足的补英文半角空格.nchar:固定长度,存储Unicode字符,不足的补英文半角空格varchar:可变长度,存储ANSI字符,根据数据长度 ...

  6. Mybatis中映射器实现方式总结

    一种是通过XML文件方式(由一个java接口和一个XML文件构成) RoleMapper rm = session.getMapper(RoleMapper.class); List<Role& ...

  7. JS发送短信验证码

    <div> <input type="tel" id="mobile" name="mobile" placeholder ...

  8. cocosStudio制作ScrollView并在cocos2dx 3.0中使用。

    使用cocosStudio制作界面基本已成为基础了,之前都是拖动一些 Image.Button的小控件,再用到层容器和滚动层的时候,习惯性的用拖动来改变控件的大小.但是你在把其他的控件拖动到上面的时候 ...

  9. NHibernate初学者指南系列文章导航

    NHibernate初学者指南系列文章导航   前面的话 经过三个多周的时间,终于将这个系列完成了,谢谢大家的关注和支持,有很多不足之处还望大家包涵. 本系列参考的书籍为NHibernate 3 Be ...

  10. HTML5表单及其验证

    随笔- 15 文章- 1 评论- 115 HTML5表单及其验证   HTML表单一直都是Web的核心技术之一,有了它我们才能在Web上进行各种各样的应用.HTML5 Forms新增了许多新控件及其A ...