小白学Java24:JDBC

环境搭建

eclipse:

  1. 找到对应的MySQL版本的JDBC库
  2. 在项目下新建源文件夹:lib
  3. 把库复制到lib文件夹下,
  4. 拖动lib 复制文件夹,生成路径—>配置路径
  5. 顶部复制“库”,右侧添加JAR,找到刚刚复制到lib文件夹里的库
  6. 应用并关闭

可能会出现的错误:

  • 时区错误:
    • 解决方法1:连接时设置时区字符串URL =jdbc:mysql://localhost:3306/books ?serverTimezone=UTC;
    • 解决方法2:修改默认时区 mysql> set global time_zone =’+ 8:00’;

开发步骤

1.加载驱动

class.forName "com.mysql.cj.jdbc.Driver"; 

2.连接到数据库

获得连接

String url = "jdbc:mysql://localhost:3306/companydb";
String user = "root";
String password = "1234";
Connection conn = DriverManager.getConnection(url,user,password);

测试是否连接成功

if(conn!= null) {
		System.out.println("连接到数据库");
	}else {
		System.out.println("连接失败");
	}

3. 获取发送SQL语句的对象

Statement Statement = conn.createStatement();

4. 执行SQL语句

DML操作(增删改): 返回的是受影响行数(int)

Statement.executeUpdate(Sql语句)

//增
String sql = "insert into test values(2,'张三');";
//删
String sql = "delete from test where id = 1;";
//改
String sql = "update test set name = '测试'where id = 1;";

int executeUpdate = Statement.executeUpdate(sql);

DQL(查询操作) :返回的是ResultSet字符集

String sql2 = "select * from test;";
ResultSet executeQuery = Statement.executeQuery(sql2);

5. 处理接收结果

if(executeUpdate == 1) {
		System.out.println("执行成功");
	}else System.out.println("执行失败");

6. 释放资源

statement.close();
conn.close();

ResultSet(查询得到的结果集)

接收结果集

String sql2 = "select * from test;";
ResultSet rs= Statement.executeQuery(sql2);

遍历ResultSet中的数据

// 根据列号 来遍历
while (re.next()) {
	int id = re.getInt(1);
	String name = re.getString(2);
	System.out.println(id + "\t" + name);
	}
	
// 根据列名 来遍历
while (re.next()) {
	int id = re.getInt("id");
	String name = re.getString("name");
	System.out.println(id + "\t" + name);
	}

小练习

编写用户注册程序

public static void main(String[] args) throws Exception {
String driver = "com.mysql.cj.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/test";
		String user = "root";
		String password = "1234";
		Connection conn = null;

		Class.forName(driver);
		conn = DriverManager.getConnection(url, user, password);
		Statement statement = conn.createStatement();

		Scanner sc = new Scanner(System.in);
		System.out.println("欢迎注册~");
		boolean flag = false;
		String username = null;
		while (!flag) {
			System.out.println("请输入用户名");
			username = sc.next();
			flag = checkout(statement, username);
		}
		System.out.println("请输入密码");
		String userpassword = sc.next();
		addlogin(statement, username, userpassword);
		
		statement.close();
		conn.close();
	}

	// 用户名是否重复
	private static boolean checkout(Statement statement, String str) {
		String sql = "select name from user;";
		try {
			ResultSet rs = statement.executeQuery(sql);
			while (rs.next()) {
				if (rs.getString("name").equals(str)) {
					System.out.println("此用户名已经被使用,请重试");
					return false;
				}
			}
			return true;

		} catch (SQLException e) {
			System.out.println("查询语句出错");
			e.printStackTrace();
		}
		return false;
	}
//插入到数据库
	private static void addlogin(Statement statement, String name, String password) {
		String sql = "insert into user(name,password) values('" + name + "','" + password + "');";
		try {
			statement.executeLargeUpdate(sql);
			System.out.println("注册成功");
		} catch (SQLException e) {
			System.out.println("插入语句出错");
			e.printStackTrace();
		}
	}

编写用户登录程序

public static void main(String[] args) throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
		String url = "jdbc:mysql://localhost:3306/test";
		String user = "root";
		String password = "1234";
		Connection conn = DriverManager.getConnection(url, user, password);

		System.out.println("欢迎登录~");
		Scanner sc = new Scanner(System.in);
		System.out.println("请输入用户名:");
		String username = sc.next();
		System.out.println("请输入密码:");
		String userpassword = sc.next();

		Statement statement = conn.createStatement();
		String sql = "select name ,password from user where name ='" + username + "'and password = '" + userpassword
				+ "';";
		ResultSet rs = statement.executeQuery(sql);
		if (rs.next()) {
			System.out.println("登录成功");
		} else {
			System.out.println("登录失败");
		}
		rs.close();
		statement.close();
		conn.close();

PreparedStatement{重点}:避免SQL注入

public static void main(String[] args) throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
		String url = "jdbc:mysql://localhost:3306/test";
		String user = "root";
		String password = "1234";
		Connection conn = DriverManager.getConnection(url, user, password);

		System.out.println("欢迎登录~");
		Scanner sc = new Scanner(System.in);
		System.out.println("请输入用户名:");
		String username = sc.next();
		System.out.println("请输入密码:");
		String userpassword = sc.next();
//使用更安全也更快的PreparedStatement
		PreparedStatement statement = conn.prepareStatement("select * from user where name = ? and password = ?;");
		statement.setString(1, username);
		statement.setString(2,userpassword);
		
		ResultSet rs = statement.executeQuery(sql);
		if (rs.next()) {
			System.out.println("登录成功");
		} else {
			System.out.println("登录失败");
		}
		rs.close();
		statement.close();
		conn.close();

封装工具类

重用性方案

public class JdbcTool {
	static {
		try {
			Class.forName("com.mysql.cj.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	public static Connection getConnection() {
		Connection conn = null;
		String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC&characterEncoding=utf-8";
		String user = "root";
		String password = "1234";
		try {
			conn = DriverManager.getConnection(url, user, password);
		}catch (SQLException e) {
			System.out.println("连接失败");
			e.printStackTrace();
		}
		return conn;

	}

	public static void closeAll(Connection conn, Statement s, ResultSet rs) {
		try {
			if (conn != null) {
				conn.close();
			}
			if (s != null) {
				s.close();
			}
			if (rs != null) {
				rs.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}

跨平台方案


public class JDBCUtils {
	private static final Properties PROPERTIES = new Properties(); // 存储配置文件的集合

	static {
		InputStream is = JDBCUtils.class.getResourceAsStream("/db.properties");//把配置文件转换成流
		try {
			PROPERTIES.load(is);// 通过流,将配置文件加载到properties集合
			Class.forName(PROPERTIES.getProperty("driver"));//通过键,获取到driver的值
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}

	}

	public static Connection getConnection() {
		Connection conn = null;
		try {
			conn = DriverManager.getConnection(PROPERTIES.getProperty("url"), PROPERTIES.getProperty("user"),
					PROPERTIES.getProperty("password"));
		} catch (SQLException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		return conn;
	}

	public static void closeAll(Connection conn, Statement statement, ResultSet resultSet) {
		try {
			if (conn != null) {
				conn.close();
			}
			if (statement != null) {
				statement.close();
			}
			if (resultSet != null) {
				resultSet.close();
			}
		} catch (SQLException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}
}

//配置文件:
//文件名: db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
user=root
password=1234

ORM: 对象关系映射

零散数据整理

一行数据中,多个零散的数据进行整理
通过entity的规则对表中的数据进行对象的封装
entity: 实体类
类名 = 表名, 属性名 = 列名,提供各个属性的get,set方法
提供无参构造方法,(视情况添加有参构造

//创建类  实现 类名=表名,属性名= 列名

//使用工具包,然后把查询到的数据存入实体类集合中
Connection conn = null;
		PreparedStatement statement = null;
		ResultSet rs = null;
		List<User> userList = new ArrayList<>();

		conn = JDBCUtils.getConnection();
		try {
			statement = conn.prepareStatement("select *from test;");
			rs = statement.executeQuery();
			while (rs.next()) {
				int id = rs.getInt("id");
				String name = rs.getString("name");
				userList.add(new User(id, name));
			}
		} catch (SQLException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		
		JDBCUtils.closeAll(conn, statement, rs);
		for (User user : userList) {
			System.out.println(user);
		}

DAO数据访问对象

  • DAO实现了业务逻辑和数据库访问的分离
    • 对同一张表的所有操作封装在XXXDaoImpl对象中
    • 根据增删改查的不同功能实现具体的方法(insert,uodate,delete,select.selectAll)

DAO步骤

  1. 创建数据表
  2. 根据数据表封装实体类,类名 = 表名,属性名=列名
  3. 编写DaoImpl类,提供增删改查的方法

Date工具类

在使用DAO的时候有个问题,数据库的日期类型为java.sql.Date,但是Java程序里的日期类型为Java.util.Date,当我们使用Java程序插入带日期的数据到数据库中时,需要进行转换

字符串\Util.Date\SQL.Date之间的转换

public class DateUtils {
	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

	// 字符串转Util.date
	public static java.util.Date strToUtilD(String str) {
		java.util.Date date = null;
		try {
			date = sdf.parse(str);
			return date;
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return date;

	}

	// util.date转sql.date
	public static java.sql.Date utilDToSqlD(java.util.Date date) {
		return new java.sql.Date(date.getTime());
	}

	// date装换为字符串
	public static String utilToSql(java.util.Date date) {
		return sdf.format(date);
	}

}

Service业务

步骤

  1. 使用ORM编写底层对象关系映射,类名=表名, 属性名=列名
  2. 编写DaoImpl类,封装数据库操作
  3. 编写ServiceImpl类,实现业务逻辑

例子:实现转账业务

  1. 在mysql中创建表并插入数据
CREATE TABLE account(
	cardNo VARCHAR(20) PRIMARY KEY,
	PASSWORD VARCHAR(20) NOT NULL,
	NAME VARCHAR(20) UNIQUE  NOT NULL,
	balance DOUBLE NOT NULL
	)CHARSET = utf8;


INSERT INTO account VALUES( '6001','1234','luyan',10000),( '6002','1234','zhang',10000);
  1. 编写ORM对象关系映射
public class Account {
	private String cardNo;
	private String password;
	private String name;
	private double balance;

	public Account() {
	
	}

	public Account(String cardNo, String password, String name, double balance) {
		super();
		this.cardNo = cardNo;
		this.password = password;
		this.name = name;
		this.balance = balance;
	}

	public String getCardNo() {
		return cardNo;
	}

	public void setCardNo(String cardNo) {
		this.cardNo = cardNo;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public double getBalance() {
		return balance;
	}

	public void setBalance(double balance) {
		this.balance = balance;
	}

	@Override
	public String toString() {
		return "Account [cardNo=" + cardNo + ", password=" + password + ", name=" + name + ", balance=" + balance + "]";
	}

}
  1. 编写AccountDaoImpl,实现与数据库的交互
public class AccountDaoImpl {
	public int insert(Account account) {
		return 0;
	}

	public int delete(String cardNo) {
		return 0;
	}

	// 修改
	public int update(Account account) {
		Connection conn = null;
		PreparedStatement statement = null;
		String sql = "update account set password =?,name = ? , balance = ? where cardNo = ?;";
		try {
			conn = JDBCUtils.getConnection();
			statement = conn.prepareStatement(sql);
			statement.setString(1, account.getPassword());
			statement.setString(2, account.getName());
			statement.setDouble(3, account.getBalance());
			statement.setString(4, account.getCardNo());
			int update = statement.executeUpdate();
			return update;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeAll(conn, statement, null);
		}
		return 0;
	}

	// 查询单个
	public Account select(String cardNo) {
		Connection conn = null;
		PreparedStatement statement = null;
		ResultSet rs = null;
		Account account = null;
		String sql = "select * from account where cardNo = ?;";
		try {
			conn = JDBCUtils.getConnection();
			statement = conn.prepareStatement(sql);
			statement.setString(1, cardNo);
			rs = statement.executeQuery();
			if (rs.next()) {
				String cardNos = rs.getString("cardNo");
				String password = rs.getString("password");
				String name = rs.getString("name");
				double balance = rs.getDouble("balance");
				account = new Account(cardNos, password, name, balance);
			}
			return account;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return account;
	}
}
  1. 编写AccountServiceImpl实现业务逻辑
public class AccountServiceImpl {

	// 转账
	public void teansfer(String fromNo, String pwd, String toNo, double money) {
		AccountDaoImpl ad = new AccountDaoImpl();
		try {
			// 验证from是否存在
			Account from = ad.select(fromNo);
			if (from == null) {
				throw new RuntimeException("卡号不存在");
			}
//		登录fromNo
			if (!from.getPassword().equals(pwd)) {
				throw new RuntimeException("密码错误!");
			}
//		验证余额
			if (from.getBalance() < money) {
				throw new RuntimeException("余额不足!");
			}
//		验证toNo
			Account to = ad.select(toNo);
			if (to == null) {
				throw new RuntimeException("转入账户不存在");
			}
//		减少from余额
			from.setBalance(from.getBalance() - money);
			ad.update(from);
//		增加TONo余额
			to.setBalance(to.getBalance() + money);
			ad.update(to);

			System.out.println("转账成功");
		} catch (RuntimeException e) {
			e.printStackTrace();
			System.out.println("转账失败!");
		}
	}
}

使用ThreadLocal实现事务的共享

使用事务

conn = JDBCUtils.getConnection();
conn.setAutoCommit(false);//关闭自动提交,开启事务

conn.commit();//提交数据
conn.rollback();//回滚数据

存在问题:

因为Connection不同步所以并不能真正实现回滚

解决方案:ThreadLocal

使用ThreadLocal可以将整个线程中存储一个共享值

//在JDBCUtils中,将当前Connection对象添加到ThreadLocal中
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();


public static Connection getConnection() {
		Connection conn = threadLocal.get();
		try {
			if (conn == null) {
				conn = DriverManager.getConnection(PROPERTIES.getProperty("url"), PROPERTIES.getProperty("user"),
						PROPERTIES.getProperty("password"));
				threadLocal.set(conn);
			}
		} catch (SQLException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		return conn;
	}

使用ThreadLocal后的JDBCUtils工具类

private static final Properties PROPERTIES = new Properties(); // 存储配置文件的集合
	private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
	static {
		InputStream is = JDBCUtils.class.getResourceAsStream("/db.properties");
		try {
			PROPERTIES.load(is);// 通过流,将配置文件加载到properties集合
			Class.forName(PROPERTIES.getProperty("driver"));
		} catch (IOException e) {

			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

	}

	public static void begin() {

		try {
			Connection conn = getConnection();
			conn.setAutoCommit(false);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public static void commit() {
		Connection conn = null;
		try {
			conn = getConnection();
			conn.commit();
		} catch (SQLException e) {

			e.printStackTrace();
		} finally {
			closeAll(conn, null, null);
		}
	}

	public static void rollback() {
		Connection conn = null;
		try {
			conn = getConnection();
			conn.rollback();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			closeAll(conn, null, null);
		}
	}

	public static Connection getConnection() {
		Connection conn = threadLocal.get();
		try {
			if (conn == null) {
				conn = DriverManager.getConnection(PROPERTIES.getProperty("url"), PROPERTIES.getProperty("user"),
						PROPERTIES.getProperty("password"));
				threadLocal.set(conn);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}

		return conn;
	}

	public static void closeAll(Connection conn, Statement statement, ResultSet resultSet) {
		try {

			if (resultSet != null) {
				resultSet.close();
			}
			if (statement != null) {
				statement.close();
			}
			if (conn != null) {
				conn.close();
				threadLocal.remove();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}

	}
}

三层架构

    1. 表示层:
      命名规则: xxxxView
      职责: 收集用户的数据和需求,展示数据
    1. 业务逻辑层
      命名规则: xxxxServiceImpl
      职责: 数据加工处理,调用DAO完成业务的实现,控制事务
    1. 数据访问层
      命名规则: xxxxDaoImpl
      职责: 向业务层提供数据,将业务层加工后的数据同步到数据库

最好使用接口来编写业务逻辑层和数据访问层

连接池:Druid

在这里插入图片描述使用连接池可以实现资源的可重复利用

//需要在配置文件中添加:
initialSize=10//初始连接数
maxActive=30//最大连接数
minIdle=5//最小保存连接数
maxWait=3000//等待可用连接时间

创建连接池

//声明一个私有的连接池对象
private static DruidDataSource ds;
//加载配置文件
Properties properties = new Properties();
InputStream is = DbUtils.class.getResourceAsStream("/db.properties");
properties.load(is);
			ds = (DruidDataSource) 
//传入配置文件信息到连接池,会自动连接			ds = (DruidDataSource)DruidDataSourceFactory.createDataSource(properties);

//获取连接对象
Connection con = ds.getConnecton();

//放回连接池
con.close();

基于Apache的DbUtils

在这里插入图片描述小白学Java24:JDBC_第1张图片

//使用apache提供的QueryRunner类,来执行SQL语句

private static QueryRunner qr = new QueryRunner(DbUtils.getDataSource());//传入一个连接池对象

//执行增删改语句:
qr.update(String sql)

//执行查询语句
qr.query(String sql);

private static QueryRunner qr = new QueryRunner(DbUtils.getDataSource());

	// 增
	private static int insert(User user) {
		Object[] o = { user.getUid(), user.getUname(), user.getUpassword(), user.getUstate(), user.getUrole() };
		try {
			return qr.update("insert into user values(?,?,?,?,?);", o);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return 0;

	}

	// 删
	private static int delete(int id) {
		try {
			return qr.update("delete from user where uid = ?;", id);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return 0;
	}

	// 改:
	private static int update(User user) {
		Object[] o = { user.getUname(), user.getUpassword(), user.getUstate(), user.getUrole(), user.getUid() };
		try {
			return qr.update("update user set uname = ?,upassword = ?,ustate=?,urole =? where uid = ? ;", o);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return 0;
	}

	// 查
	private static User select(int id) {
		try {
			// 把查询到的数据封装成指定对象
			return qr.query("select * from user where uid = ?;", new BeanHandler<User>(User.class), id);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}

	// 查所有
	private static List<User> selectAll() {
		try {
			return qr.query("select * from user;", new BeanListHandler<User>(User.class));
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}

你可能感兴趣的:(学习笔记,小白学Java)