相关jar包
相关数据库
users
druid.properties
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver
initialSize=10
maxActive=20
maxWait=1000
utils
/*
* 问题1:注册驱动,获取连接等写了很多遍
* 问题2:我们每次都从数据库获取新的连接
* mysql是一个TCP/IP协议的网络程序,那么:
* (1)每获取一次新的连接的成本很高,需要“三次握手”,断开需要“四次挥手”等过程
* (2)每一个客户端都有单独的线程来维护它的通信
* 这样就会造成如果有很多的客户端同时去连接mysql服务器,会造成
* (1)mysql的并发量就有风险,如果太多就会挂了,
* 特别是遇到一些程序员,获取完连接,没有关闭。
* (2)每次高成本获取的连接只用一次,太奢侈了,我们希望可以重复使用连接对象。
*
*
* 解决:
* (1)解决问题1:我们把注册驱动,获取连接等方法,封装到一个工具类中,减少代码
* (2)解决问题2:我们可以使用“数据库连接池”来解决
*
* 数据库连接池的技术:
* (1)先创建一个连接池pool,然后在池中先放一些连接对象,然后程序去获取连接对象时,用先有的对象,会更快。
* 例如:从自来水新接一桶水,没有从游泳池中直接“装”一桶水快。
*
* (2)然后我们可以设置连接池的最大连接数量,如果池中的所有连接都在使用的话,那么可以让“客户端”等待,
* 虽然有等待的现象,但是总被“挂”了好。
*
* (3)创建连接池时,一开始是初始化少量的连接,等用户并发量上来后,会增加连接数,直到最高连接数为止。
*
* (4)之前conn.close()真正的与服务器断开连接,现在从连接池中拿的连接对象,关闭时是还给连接池。
*
* 连接池技术有很多,我们讲的是德鲁伊,它是阿里的。
*
* 使用步骤:
* (1)引入"德鲁伊"jar
* druid-1.1.10.jar
*
* (2)加一个配置文件
* 配置“德鲁伊"连接池的参数
*
* 明确:数据库连接池的作用,管理连接。
* 为了获取连接,需要哪些参数:主机名、端口号、用户名、密码、驱动类名
* 其他参数:初始化连接数,最多连接数...
*
* 在src下建一个druid.properties文件
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver
initialSize=10
maxActive=20
maxWait=1000
filters=wall
*
* (3)创建连接池(一个系统一个)
*
* javax.sql.DataSource
*
* (4)提供一个方法,可以从数据库连接池中拿连接对象
* (5)提供一个关闭连接的方法
*/
public class JdbcUtils {
private static DataSource ds ;
private static ThreadLocal<Connection> local;
static{
try {
//静态代码块可以初始化静态变量
// (1)把src下建一个druid.properties文件的数据,加载到一个Properties对象中
Properties pro = new Properties();
pro.load(JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
ds = DruidDataSourceFactory.createDataSource(pro);
local = new ThreadLocal<>();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//这个代码不能保证同一个线程(客户端)共享同一个连接对象,我们后面的代码如果需要事务处理,就有问题
/* public static Connection getConnection() throws SQLException{
//这里只是改变获取连接的代码
return ds.getConnection();
}*/
//这里修改为用ThreadLocal来保存同一个线程的共享变量
public static Connection getConnection() throws Exception{
//这里只是改变获取连接的代码
Connection conn = local.get();//如果从local中能够得到一个连接对象,那么说明当前线程已经拿过了
if(conn == null){//如果不能得到一个连接对象,那么说明当前线程没拿过了
conn = ds.getConnection();
local.set(conn);
}
return conn;
}
//提供关闭连接的方法
public static void free(){
try {
Connection conn = local.get();
if(conn!=null){
local.remove();
conn.setAutoCommit(true);//还原我们的连接为自动提交模式,等别人再次拿到这个连接时,就可以默认是自动提交
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static DataSource getDs() {
return ds;
}
}
BasicDao. (dbutils)
/*
* apache工具包,有一个ResultSetHandler接口,结果集处理器的接口
* ResultSetHandler接口的实现类们:
* (1)BeanListHandler:结果是一个List<T>
* (2)BeanHandler:结果是一个Javabean对象
* (3)MapHandler:结果是一行多列,但是又不是Javabean对象
* (4)MapListHandler:结果是多行多列,但是又不是Javabean对象
* (5)ScalarHandler<T>:单个值的封装类型
*
*/
public class BasicDAO2 {
private QueryRunner qr = new QueryRunner(JdbcUtils.getDs());
//适用于insert,update,delete语句
public int update(String sql,Object... args) throws Exception{
return qr.update(sql, args);
}
//查询多个Javabean
public <T> List<T> getList(Class<T> clazz, String sql,Object... args)throws Exception{
return qr.query(sql, new BeanListHandler<T>(clazz), args);
}
//查询一个Javabean,一般跟着主键,唯一键查询
public <T> T getBean(Class<T> clazz, String sql,Object... args)throws Exception{
return qr.query(sql, new BeanHandler<T>(clazz), args);
}
//例如:查询总记录数,最高工资,最低工资,平均工资,。。。单个值
public Object queryObject(String sql, Object... args)throws Exception{
return qr.query(sql, new ScalarHandler<>(), args);
}
//查询多行多列,例如:按部门查询平均工资
public List<Map<String, Object>> queryListMap(String sql, Object... args)throws Exception{
return qr.query(sql, new MapListHandler(), args);
}
//查询一行多列,例如:查询某一个部门的平均工资
public Map<String, Object> queryMap(String sql, Object... args)throws Exception{
return qr.query(sql, new MapHandler(), args);
}
}
impl 实现类
public class UserDAOImpl extends BasicDAO2 implements UserDAO{
//这个方法根据用户名和密码获取数据库中的记录
@Override
public User getUser(User user) {
try {
String sql = "SELECT * FROM w WHERE username = ? AND `password` = ?";
//查询一个对象
return getBean(User.class, sql, user.getUsername(),user.getPassword());
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
@Override
public boolean checkUserName(User user) {
try {
String sql = "SELECT * FROM users WHERE username = ?";
//查询一个对象
User u = getBean(User.class, sql, user.getUsername());
return u!=null;//如果不为空,说明存在,为true
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
@Override
public void saveUser(User user) {
try {
String sql = "INSERT INTO users VALUES(NULL,?,?,?)";
//查询一个对象
update(sql, user.getUsername(),user.getPassword(),user.getEmail());
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
}
Test类
public class TestUserDAO {
UserDAOImpl ud = new UserDAOImpl();
@Test
public void test1(){
//登录
Scanner input = new Scanner(System.in);
System.out.println("用户名:");
String username = input.nextLine();
System.out.println("密码:");
String password = input.nextLine();
User user = new User(username, password);
User u = ud.getUser(user);
System.out.println(u);
}
@Test
public void test2(){
String username = null;
String password = null;
//登录
Scanner input = new Scanner(System.in);
while(true){
System.out.println("用户名:");
username = input.nextLine();
User user = new User();
user.setUsername(username);
//检验这个用户名是否存在
if(!ud.checkUserName(user)){
break;
}else{
System.out.println("用户名已存在");
}
}
while(true){
System.out.println("密码:");
password = input.nextLine();
System.out.println("确认密码:");
String confirm = input.nextLine();
if(password.equals(confirm)){
break;
}else{
System.out.println("两次输入密码要一致");
}
}
System.out.println("邮箱:");
String email = input.nextLine();
User u = new User();
u.setUsername(username);
u.setPassword(password);
u.setEmail(email);
try {
//注册,添加到数据库中
ud.saveUser(u);
System.out.println("注册成功");
} catch (Exception e) {
System.out.println("注册失败");
}
}
}