mysql之jdbc建立封裝ORM及配置檔案的書寫
阿新 • • 發佈:2020-12-22
技術標籤:Mysql
JDBC建立的方式
1、載入驅動通過配置檔案
2、連線資料庫獲取連線物件
3、Statement/PrepareStatement獲得預編譯物件
4、執行sql語句得到結果集
5、處理結果集
6、關閉資源
配置檔案
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.116.1:3306/lee?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false
user=root
password= 123456
//註冊驅動載入驅動獲取連線物件
Class.forName(p.getProperty("driverClass"));
String url=p.getProperty("url");
String username=p.getProperty("user");
String password=p.getProperty("password");
Connection connection= DriverManager.getConnection(url,username,password);
案例
@Override
public int in(String inName,Double money) {
Connection conn=null;
PreparedStatement ps=null;
try {
connection.setAutoCommit(false);
//獲取連線將獲取連線封裝在DBUtils的getConnection方法裡
conn = DBUtils.getConnection();
//預編譯
ps= conn.prepareStatement("update t_account set balance=balance+? where name=?");
//給佔位符賦值
ps.setObject(1,money);
ps.setObject(2,inName);
//執行sql語句得到結果
int i = ps.executeUpdate();
//處理結果集
return i;
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
//關閉資源,將關閉資源封裝在DBUtils的close方法裡
DBUtils.closeAuto(ps);
} catch (Exception e) {
e.printStackTrace();
}
}
return 0;
}
PrepareStatement和Statement的區別
prepareStatement進行預編譯處理,重要引數用佔位符?代替,預編譯之後再給佔位符賦值,可以防止SQL注入問題
//預編譯
ps= conn.prepareStatement("update t_account set balance=balance+? where name=?");
//給佔位符賦值
ps.setObject(1,money);
ps.setObject(2,inName);
封裝工具類
將註冊載入驅動獲取連線和關閉連線釋放資源封裝起來
public class DBUtils {
//增加threadlocal
//一個執行緒對應一個ThreadLocal
//public static DruidDataSource druidDS;
//ThreadLocal的作用是保證一個執行緒裡獲取的連線都是同一個連線,用於處理事務
public static ThreadLocal<Connection> threadLocal=new ThreadLocal<>();
public static Connection getConnection() throws Exception {
if (threadLocal.get()==null){
Properties p=new Properties();
p.load(new FileInputStream("u.properties"));
// druidDS= (DruidDataSource) DruidDataSourceFactory.createDataSource(p);
// DruidPooledConnection connection1 = druidDS.getConnection();
//載入註冊驅動獲取連線
Class.forName(p.getProperty("driverClass"));
String url=p.getProperty("url");
String username=p.getProperty("user");
String password=p.getProperty("password");
Connection connection= DriverManager.getConnection(url,username,password);
//將獲取的連線放入ThreadLocal裡
threadLocal.set(connection1);
}else {
return threadLocal.get();
}
return threadLocal.get();
}
public static void closeAuto(AutoCloseable...cs) throws Exception {
for (AutoCloseable cc: cs) {
//判斷是否為空,不為空就釋放
if (cc!=null){
//判斷資源是否是connection,如果是,則先從threadlocal裡移除再關閉資源
if (!(cc instanceof Connection)){
cc.close();
}else {
threadLocal.remove();
cc.close();
}
}
}
}
//獲取連線的方式一
Properties p=new Properties();
p.load(new FileInputStream("u.properties"));
//------------------------------------------
//獲取連線的方式二
Properties p=new Properties();
InputStream resourceAsStream = DBUtils.class.getResourceAsStream("/db.properties");
p.load(resourceAsStream);
Class.forName(p.getProperty("driverClass"));
String url=p.getProperty("url");
String username=p.getProperty("user");
String password=p.getProperty("password");
Connection connection= DriverManager.getConnection(url,username,password);
ORM思想
下載Lombok外掛並且導jar包可以自動寫入get、set、構造方法
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
private int id;
private String name;
private String password;
private double balance;
}
將處理的結果集進行封裝,封裝到實體類物件中
@Override
public Account selectAccountByName(String name) {
Connection conn=null;
PreparedStatement ps=null;
ResultSet resultSet=null;
try {
conn=DBUtils.getConnection();
ps=conn.prepareStatement("select * from t_account where name=?");
ps.setObject(1,name);
resultSet = ps.executeQuery();
if (resultSet.next()){
/**對獲取的結果集進行封裝,返回物件*/
int selectId=resultSet.getInt("id");
String selectName=resultSet.getString("name");
String selectPassword=resultSet.getString("password");
Double selectBalance=resultSet.getDouble("balance");
Account account=new Account(selectId,selectName,selectPassword,selectBalance);
return account;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
DBUtils.closeAuto(resultSet,ps);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
架構
View層(檢視層)->Controller層(控制業務邏輯)->Service層(業務邏輯實現)->Dao層(和資料庫進行互動)->DB(資料庫)
Date工具類
主要是對java.util的時間和java.sql的時間進行轉換
Date工具類
主要是對java.util的時間和java.sql的時間進行轉換
@Override
public User Together(Scanner input) throws ParseException {
User user=new User();
System.out.println("請輸入密碼");
String password=input.next();
System.out.println("請輸入年齡");
int age=input.nextInt();
System.out.println("請輸入性別");
int sex=input.nextInt();
System.out.println("請輸入生日");
String birthday=input.next();
//將字串轉換成util的Date存到物件中
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd");
java.util.Date birthdayData = simpleDateFormat.parse(birthday);
user.setBirthday(birthdayData);
user.setPassword(password);
user.setAge(age);
user.setSex(sex);
return user;
}
}
@Override
public int add(String name) throws ParseException {
Connection conn=null;
PreparedStatement ps=null;
User user = selectUserByName(name);
if (user==null){
if (name.equals("null")||name.length()==0){
return 0;
}
user=Together(input);
user.setName(name);
try {
conn = DBUtils.getConnection();
ps=conn.prepareStatement("insert into t_user(username,password,age,sex,birthday) values (?,?,?,?,?)");
ps.setObject(1,user.getName());
ps.setObject(2,user.getPassword());
ps.setObject(3,user.getAge());
ps.setObject(4,user.getSex());
//utilDate型別轉換成sql的Date型別,Date(util)->long->Date(sql)
//物件中取出util的Date,轉換成long型再轉成sql的date型
ps.setObject(5,new Date(user.getBirthday().getTime()));
int i = ps.executeUpdate();
if (i>0){
return i;
}else {
return 0;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
DBUtils.closeAuto(ps,conn);
} catch (Exception e) {
e.printStackTrace();
}
}
}else {
// System.out.println("此使用者已經存在");
return 0;
}
return 0;
}
@Override
public ArrayList<User> selectAll() {
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn=DBUtils.getConnection();
ps = conn.prepareStatement("select * from t_user");
rs=ps.executeQuery();
ArrayList<User> list=new ArrayList<>();
while(rs.next()){
//結果集得到的sql型別的date可以直接裝箱成父類util的Date
String username = rs.getString("username");
String password = rs.getString("password");
int age = rs.getInt("age");
int sex = rs.getInt("sex");
Date birthday = rs.getDate("birthday");
User user=new User(username,password,age,sex,birthday);
list.add(user);
}
return list;
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
DBUtils.closeAuto(rs,ps,conn);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
事務的處理
事務的處理需要保證連線是同一個連線
方法一:連線作為引數
方法二:ThreadLocal的使用
事務的四個特性:
1、原子性 同時成功或者同時失敗
2、一致性 一個加錢一個減錢
3、隔離性 事務之間不相互影響
4、永續性 對資料庫一旦操作就永久影響
問題:
髒讀 未提交就能讀到
不可重複讀 提交之後事務沒重新開啟就能讀到
幻讀
解決:設定隔離級別
Connection connection=null;
// try {
try {
//保證同一個連線
connection = DBUtils.getConnection();
//開啟事務裡面設定自動提交為false
DBUtils.start();
sql="update t_account set balance=balance+? where name=?";
//加錢
int in = dao.commonUpdate(sql, money,inName);
// int i= 10/0;
sql="update t_account set balance=balance-? where name=?";
//扣錢
int out = dao.commonUpdate(sql, money,name);
System.out.println("轉賬成功");
//封裝事務提交
DBUtils.commit();
return 1;
} catch (Exception e) {
//封裝事務回滾
DBUtils.rollback();
System.err.println("轉賬失敗");
}finally {
try {
DBUtils.closeAuto(connection);
} catch (Exception e) {
e.printStackTrace();
}
}
return 0;
通用方法的修改
public int commonUpdate(String sql,Object...args){
Connection conn=null;
PreparedStatement ps=null;
try {
// connection.setAutoCommit(false);
conn = DBUtils.getConnection();
ps= conn.prepareStatement(sql);
for (int i=0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
int i = ps.executeUpdate();
return i;
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
DBUtils.closeAuto(ps);
} catch (Exception e) {
e.printStackTrace();
}
}
return 0;
}
@Override
public <T> ArrayList<T> commonSelect(String sql, Class<T> clazz, Object ...args) {
Connection conn=null;
PreparedStatement ps=null;
ResultSet resultSet=null;
ArrayList<T> list=null;
try {
conn=DBUtils.getConnection();
//"select * from t_account where name=?"
ps=conn.prepareStatement(sql);
for (int i=0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
//得到所有屬性,前提所有欄位和屬性一致
Field[] declaredFields = clazz.getDeclaredFields();
resultSet = ps.executeQuery();
//方法二獲得元資料
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
while (resultSet.next()){
T t=clazz.newInstance();
list=new ArrayList<>();
//方法二
for (int i=0;i<columnCount;i++){
//得到結果集的值
Object value = resultSet.getObject(i + 1);
//得到元資料的列
String columnLabel = metaData.getColumnLabel(i + 1);
//通過欄位列名得到物件對應屬性
Field declaredField = clazz.getDeclaredField(columnLabel);
declaredField.setAccessible(true);
declaredField.set(t,value);
}
//通過反射得到所有屬性,如果名字不對應會抓異常,通過讀取配置檔案的相應的屬性名取獲取資料庫結果集的對應欄位的值
// for (int i=0;i<declaredFields.length;i++){
// //得到物件屬性的名字
// String fieldName = declaredFields[i].getName();
// declaredFields[i].setAccessible(true);
// Object value = resultSet.getObject("fieldName");
// declaredFields[i].set(t,value);
// }
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
DBUtils.closeAuto(resultSet,ps);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}