文章目錄
一、今日內容
- mybatis自帶數據源
- 事務問題
- 動態的sql語句:if標籤和循環標籤 (重點)
- 多表之間的關係配置 一對一 一對多 多對多 (重點)
二、mybatis自帶數據源
<!--數據庫環境
default:指定默認環境
-->
<environments default="development">
<!--環境唯一的標誌-->
<environment id="development">
<!--事務管理:使用jdbc的事務-->
<transactionManager type="JDBC"/>
<!--
dataSource:數據源(又稱數據庫連接池)
type="POOLED":數據源類型配置 用得最多★
POOLED: 使用mybatis的自帶數據源配置
UNPOOLED: 不使用數據源配置 (非數據庫連接池) 使用Connection操作數據庫
JNDI: JNDI服務 數據源配置(麻煩,少用)
-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb_331"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
三、事務
原始jdbc的事務
關鍵行:
24行: 設置事務手動提交(不能自動提交)
36行: 提交事務
40行: 異常回滾事務
49行: 還原事務默認自動提交的狀態
public class TestJDBC {
@Test
public void test(){
List<User> userList = new ArrayList<>();
//1. 註冊驅動
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String url = "jdbc:mysql://localhost:3306/mybatisdb_331";
String username = "root";
String password = "root";
Connection conn = null;
PreparedStatement pst = null;
ResultSet rs = null;
try {
//2. 獲取連接
conn = DriverManager.getConnection(url, username ,password);
//事務1 :設置事務手動提交(不能自動提交)
conn.setAutoCommit(false);
// 3. SQL語句
String sql1 = "insert into ......";
String sql2 = "insert into ......";
//4. 創建statement對象: Statement , PreparedStatement
pst = conn.prepareStatement(sql1);
//5. 執行SQL語句,返回結果集
pst.executeUpdate();
pst = conn.prepareStatement(sql2);
pst.executeUpdate();
//事務2:提交事務
conn.commit();
} catch (SQLException e) {
//事務3:出現異常,回顧
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
//事務4: 還原狀態,設置事務爲自動提交
if(conn != null){
try {
conn.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
}
//7. 釋放資源: 先開後關
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(pst != null){
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//打印結果
for (User user : userList) {
System.out.println(user);
}
}
}
mybatis內置的事務管理(幫你寫好了)
通過日誌查看,原理和jdbc原生事務一樣
之前寫修改都要手動sqlSession.commit();太麻煩,其實openSession()時傳入參數true設置默認自動提交,就不需要每次commit了
openSession(true),避免手動提交事務(壞處:關閉了事務)
四、動態sql語句
今日pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.ahpu</groupId>
<artifactId>mybatis_day03_5_many2many</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</project>
1、問題
UserDao.xml中的sql
<!--多條件查詢-->
<select id="findByCondition" resultMap="users" resultType="user">
select * from user where uname like "%"#{username}"%" and sex=#{sex}
</select>
測試類中的方法
@Test
public void testFindByCondition(){
//獲取sqlSession對象
SqlSession sqlSession = new SqlSessionFactoryBuilder().build(this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml")).openSession();
//獲取動態代理對象
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = new User();
//user.setUsername("英");//註釋本行報錯
user.setSex("女");//只註釋本行 不報錯 但查詢結果爲null
List<User> list = userDao.findByCondition(user);
for (User u : list) {
System.out.println(u);
}
//關閉資源
sqlSession.close();
}
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'null"%" and sex='女'' at line 1
當需要多個參數,卻傳入少數幾個參數,也即有需要傳入的參數爲null時,要麼報錯,要麼查不出結果。都是傳參不過導致sql語句拼接後出了問題
解決方法就是使用動態sql語句,sql語句內也寫上邏輯判斷 應該是OGNL
2、if語句
寫法1: 技巧性寫法 1=1 然後都加and 無論咋拼接都沒有sql語法錯誤
<select id="findByCondition" resultMap="users" resultType="user">
select * from user where 1=1
<if test="username != null">
and uname like "%"#{username}"%"
</if>
<if test="sex != null">
and sex=#{sex}
</if>
</select>
寫法2: mybatis內部解決方案 不寫where而使用where標籤
3、where語句
<!--<where>標籤只會幫我們處理第一個and
因此都加上and最安全
-->
<select id="findByCondition" resultMap="users" resultType="user">
select * from user
<where>
<if test="username != null">
and uname like "%"#{username}"%"
</if>
<if test="sex != null">
and sex=#{sex}
</if>
</where>
</select>
4、SQL片段
重複的sql語句不想總是寫 可以提取出爲sql片段
<!--sql片段
把重複的sql語句提取出來,需要使用時引用即可
id:唯一標識
文本:sql語句
-->
<sql id="select_user">select * from user</sql>
<select id="findByCondition" resultMap="users" resultType="user">
-- 引入sql片段
<include refid="select_user"></include>
<where>
<if test="username != null">
and uname like "%"#{username}"%"
</if>
<if test="sex != null">
and sex=#{sex}
</if>
</where>
</select>
以後遇到很長且需要重複使用的sql語句時可以這麼做
5、foreach語句
根據integer數組ids刪除指定多個id的記錄
根據數組刪除
xml中的sql
<delete id="delByArray" parameterType="int[]">
delete from user where
<!--
foreach循環標籤
collection: 參數類型 取值:數組用 array 集合用: list
open: 前綴 (以...開始)
close: 後綴 (以...結束)
separator: 分隔符
item: 循環中的每一個對象 (名字隨便取)
index: 循環中的索引(一般不用)
-->
<!-- uid in (1,2,3) -->
<foreach collection="array" open="uid in (" close=")" separator="," item="id">
#{id}
</foreach>
</delete>
測試代碼:
@Test
public void testDelByArray(){
//獲取sqlSession對象
SqlSession sqlSession = new SqlSessionFactoryBuilder().build(this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml")).openSession();
//獲取動態代理對象
UserDao userDao = sqlSession.getMapper(UserDao.class);
Integer[] ids=new Integer[]{7,15};
userDao.delByArray(ids);
sqlSession.commit();
//關閉資源
sqlSession.close();
}
根據集合刪除
同上例子
xml中的sql
<delete id="delByList" parameterType="list">
DELETE FROM USER WHERE
/*uid in (1,2,3)*/
<foreach collection="list" open="uid in (" close=")" separator="," item="id">
#{id}
</foreach>
</delete>
測試代碼
@Test
public void testDelByList(){
//獲取sqlSession對象
SqlSession sqlSession = new SqlSessionFactoryBuilder().build(this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml")).openSession();
//獲取動態代理對象
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<Integer> list=new ArrayList<Integer>();
list.add(20);list.add(21);
userDao.delByList(list);
sqlSession.commit();
//關閉資源
sqlSession.close();
}
五、多表關聯(查詢所有數據,想法映射到每個屬性)
1、一對一
新建account表:
create table account(
id int primary key auto_increment,
name varchar(50) unique,
money float(8,2),
u_id int,
foreign key(u_id) references user(uid)
);
插入數據:
需求: 一個賬戶對應一個用戶,查詢所有賬戶,同時包含其對應的那個用戶
一對一查詢1: 新建pojo 繼承自A類 屬性同B類 (超級麻煩寫法)
就是新建一個javaBean,使得包含兩張表對應的所有屬性,可以繼承自一個類(java不支持多繼承,因此另一個類屬性只得慢慢寫了)
Account.java
package cn.ahpu.domain;
/**
* @author 寒面銀槍
* @create 2020-01-08 16:52
*/
public class Account {
private Integer id;
private String name;
private float money;
private Integer u_id;
//省略get/set
@Override
public String toString() {
return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + ", u_id=" + u_id + '}';
}
}
AccountUser.java(額外專門寫一個pojo 煩)
/**
* 繼承Account便擁有了Account的所有屬性
* 單獨添加user屬性
*/
public class AccountUser extends Account {
//再寫上所有user屬性
private Integer uid;
private String username;
private String password;
private String address;
private String sex;
private Date birthday;
//省略get/set/toString
}
AccountDao.java
//查詢所有的賬戶 包含對應用戶信息
public List<AccountUser> findAllAccountUser();
UserDao.xml (sql語句配置)
<mapper namespace="cn.ahpu.dao.AccountDao">
<resultMap id="accountUsers" type="accountUser">
<result column="uname" property="username"></result>
</resultMap>
<select id="findAllAccountUser" resultMap="accountUsers">
select * from account a,user u where a.u_id=u.uid
</select>
</mapper>
TestMybatisOne2One.java 測試類
@Test
public void testFindAllAccountUser(){
//獲取sqlSession對象
SqlSession sqlSession = new SqlSessionFactoryBuilder().build(this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml")).openSession();
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
List<AccountUser> list = accountDao.findAllAccountUser();
//AccountUser繼承自Account 沒事!下可以接着寫Account
for (Account au : list) {
System.out.println(au);
}
//關閉資源
sqlSession.close();
}
運行結果:
AccountUser{uid=1, username='zhangsan', password='zhangsan', address='蜀山', sex='男', birthday=null} Account{id=1, name='1234564156', money=100.0, u_id=1}
AccountUser{uid=2, username='lisi', password='lisi', address='天墉城', sex='男', birthday=null} Account{id=2, name='242415', money=1000.0, u_id=2}
AccountUser{uid=10, username='天河', password='tianhe', address='青鸞峯', sex='男', birthday=Tue Jan 07 16:15:23 CST 2020} Account{id=3, name='24235698', money=200.0, u_id=10}
一對一查詢2: 直接在一方寫另一方的類對象爲屬性(稍正常寫法)
Account.java
public class Account {
private Integer id;
private String name;
private float money;
private Integer u_id;
//一個賬戶對應一個用戶
private User user;
//省略get/set
}
沒有AccountUser了
AccountDao 接口方法
//查詢所有的賬戶 包含對應用戶信息
public List<Account> findALL();
AccountDao.xml sql配置 ★ 唯一麻煩都是倒騰返回值
<!--查詢所有用戶 需要保護所屬客戶信息 但不必創建新的pojo-->
<!--複雜pojo需要配置下-->
<resultMap id="accounts" type="Account">
<!--配置時property和column完全相同的都可省略-->
<!--<id property="id" column="id"></id>
<result column="name" property="name"></result>
<result column="money" property="money"></result>
<result column="u_id" property="u_id"></result>-->
<!--映射User中的屬性 也是User與user表的映射-->
<!--property和column都不完全相同 都不能省略-->
<result column="uid" property="user.id"></result>
<result column="uname" property="user.username"></result>
<result column="password" property="user.password"></result>
<result column="sex" property="user.sex"></result>
<result column="address" property="user.address"></result>
<result column="birthday" property="user.birthday"></result>
</resultMap>
<select id="findALL" resultMap="accounts">
select * from account a,user u where a.u_id=u.uid
</select>
TestMybatisOne2One 測試方法
@Test
public void testFindALL(){
//獲取sqlSession對象
SqlSession sqlSession = new SqlSessionFactoryBuilder().build(this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml")).openSession();
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
List<Account> list = accountDao.findALL();
for (Account a : list) {
System.out.println(a);
}
//關閉資源
sqlSession.close();
}
一對一查詢3: resultMap中使用association(全屬性映射 今後寫法★ 次日改進)
**所有屬性都得一一映射 一個都不能省略 好處是結構很清晰 今後都用此 累就累點吧 **
<!--方法3 更改改 resultMap 的映射
用到了 association 則所有的映射不管相同不相同 都得一個個配置 -->
<resultMap id="accounts" type="Account">
<!--property與column完全相同的也需要映射-->
<id property="id" column="id"></id>
<result column="name" property="name"></result>
<result column="money" property="money"></result>
<result column="u_id" property="u_id"></result>
<!--映射單個對象User-->
<association property="user" javaType="user">
<id property="id" column="uid"></id>
<result column="uname" property="username"></result>
<!--property與column完全相同的也需要映射-->
<result column="password" property="password"></result>
<result column="sex" property="sex"></result>
<result column="address" property="address"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>
<select id="findALL" resultMap="accounts">
select * from account a,user u where a.u_id=u.uid
</select>
其他同二
2、一對多
現在的情境是一個用戶可以有多個賬戶
一個用戶對應多個賬戶,查詢用戶同時查到他下面的所有賬戶
上面是操作Account,接下來則是操作User
UserDao 接口方法
//返回所有user對象 包含用戶對應的賬戶信息
public List<User> findAll();
UserDao.xml sql配置 ★ 以後多表操作不管那麼多 都給全屬性配置上 此處是一對多配置
<mapper namespace="cn.ahpu.dao.UserDao">
<resultMap id="users" type="user">
<id property="id" column="uid"></id>
<result property="username" column="uname"></result>
<result property="password" column="password"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<result property="birthday" column="birthday"></result>
<!--List<Account>映射 collection
property:屬性名
ofType:集合中的元素類型 (類似 association中的 javaType)
-->
<collection property="accounts" ofType="account">
<id column="id" property="id"></id>
<result property="name" column="name"></result>
<result property="money" column="money"></result>
<result property="u_id" column="u_id"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="users">
/*select * from user,account where u_id=uid; 沒有賬戶的不符合條件的無法顯示 記得where變on*/
select * from user left join account on u_id=uid
</select>
</mapper>
TestMybatisOne2Many 測試方法
/**
* 查詢所有用戶 包含賬戶信息
*/
@Test
public void testFindAll(){
//獲取sqlSession對象
SqlSession sqlSession = new SqlSessionFactoryBuilder().build(this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml")).openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> list = userDao.findAll();
for (User user : list) {
System.out.println(user);
}
//關閉資源
sqlSession.close();
}
3、多對多
增加角色,用戶和角色多對多(必須引入中間表)
用戶與角色多對多,查詢所有用戶同時能得到其下所有role。或查詢所有角色同時得到每個角色有哪些用戶
創建角色表
create table role(
id int PRIMARY key auto_increment,
roleName varchar(20),
roleDesc varchar(20)
);
創建中間表
-- 中間表
create table user_role(
uid int,
rid int,
primary key(uid,rid),
foreign key(uid) REFERENCES user(uid),
foreign key(rid) REFERENCES role(id)
);
Role.java
public class Role {
private Integer id;
private String roleName;
private String roleDesc;
//一個角色對應多個用戶
private List<User> users;
//省略get/set
}
User.java
public class User {
private Integer id;
private String username;
private String password;
private String address;
private String sex;
private Date birthday;
//一個用戶多個角色
private List<Role> roles;
//省略get/set
}
先從User一方考慮:
UserDao.java
//返回所有user對象 包含用戶對應的角色
public List<User> findAll();
先練習下sql語句
select * from user u,user_role ur where u.uid=ur.uid;
select * from user u left join user_role ur where u.uid=ur.uid;
select * from (user u left join user_role ur on u.uid=ur.uid) left join role r on ur.rid=r.id;
select u.*,r.* from (user u left join user_role ur on u.uid=ur.uid) left join role r on ur.rid=r.id;
UserDao.xml sql
<mapper namespace="cn.ahpu.dao.UserDao">
<resultMap id="users" type="user">
<id column="uid" property="id"></id>
<result column="uname" property="username"></result>
<result column="password" property="password"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
<result column="address" property="address"></result>
<!--配置角色集合-->
<collection property="roles" ofType="role">
<id column="id" property="id"></id>
<result column="roleName" property="roleName"></result>
<result column="roleDesc" property="roleDesc"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="users">
select u.*,r.* from (user u left join user_role ur on u.uid=ur.uid) left join role r on ur.rid=r.id;
</select>
</mapper>
TestMybatisMany2Many.java 測試類
/**
* 查詢所有用戶 包含角色信息
*/
@Test
public void testFindAll(){
//獲取sqlSession對象
SqlSession sqlSession = new SqlSessionFactoryBuilder().build(this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml")).openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> list = userDao.findAll();
for (User user : list) {
System.out.println(user);
}
//關閉資源
sqlSession.close();
}
再從role一方考慮(和前面一樣 雷同)
RoleDao.java 接口方法
//查詢所有角色 包含用戶對象
public List<Role> findAll();
RoleDao.xml sql配置 ★
<mapper namespace="cn.ahpu.dao.RoleDao">
<resultMap id="roles" type="role">
<id column="id" property="id"></id>
<result column="roleName" property="roleName"></result>
<result column="roleDesc" property="roleDesc"></result>
<!--配置屬性:用戶集合-->
<collection property="users" ofType="user">
<id column="uid" property="id"></id>
<result column="uname" property="username"></result>
<result column="password" property="password"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
<result column="address" property="address"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="roles">
select r.*,u.* from (role r left join user_role ur on r.id=ur.rid) left join user u on ur.uid=u.uid;
</select>
</mapper>
TestMybatisMany2Many 測試java方法
/**
* 查詢所有角色 包含用戶信息
*/
@Test
public void test2FindAll(){
//獲取sqlSession對象
SqlSession sqlSession = new SqlSessionFactoryBuilder().build(this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml")).openSession();
RoleDao roleDao = sqlSession.getMapper(RoleDao.class);
List<Role> list = roleDao.findAll();
for (Role role : list) {
System.out.println(role);
}
//關閉資源
sqlSession.close();
}
六、總結
1. 連接池(數據源):
type: POOLED,UNPOOLED ,JNDI
2. 事務問題
1) 設置手動提交(開啓事務)
2) 提交(提交事務)
3) 回滾事務
4) 設置自動提交(還原狀態)
openSession() -- 不可以自動提交事務的SqlSession對象
openSession(true) -- 可以自動提交事務的SqlSession對象
3. 動態sql語句
if 多條件查詢
where 幫助程序員處理第一個and符號,結合if語句使用
sql片段: 提取常用的代碼
foreach: 循環
屬性:collection="list | array", item="循環中每一個對象" open="前綴" close="後綴" seperator="分隔符"
4. 關係映射-- 一對一: 一個賬戶對應一個用戶
1) 聲明一個pojo,包含賬戶和用戶所有的屬性
2) 在account聲明一個User的屬性
<result property="user.屬性" column>
3)在account聲明一個User的屬性
<association property="屬性名" javaType="類型">
<result property="屬性" column>
</association>
5. 關係映射-- 一對多: 一個用戶對應多個賬戶
1) 在user類中聲明一個賬戶的集合屬性
2) <collection property="屬性名" ofType="類型">
<result property="屬性" column>
</collection>
6. 關係映射-- 多對多:兩個一對多
關係映射核心:sql語句查詢到所有數據,折騰好返回值類型使查到的數據能一一映射到對象屬性