1.表與表之間的關系及其舉例
表之間的關系有4種:一對多、多對一、一對一、多對多。
舉例:
(1)用戶和訂單就是一對多
一個用戶可以下多個訂單
(2)訂單和用戶就是多對一
多個訂單屬於同一個用戶
(3)人和身份證號就是一對一
一個人只能有一個身份證號
一個身份證號只能屬於一個人
(4)老師和學生之間就是多對多
一個學生可以被多個老師教過
一個老師可以交多個學生
2.mybatis中的多表查詢
示例:用戶和賬戶
一個用戶可以有多個賬戶
一個賬戶只能屬於一個用戶(多個賬戶也可以屬於同一個用戶)
步驟:
1、建立兩張表:用戶表,賬戶表
讓用戶表和賬戶表之間具備一對多的關系:需要使用外鍵在賬戶表中添加
2、建立兩個實體類:用戶實體類和賬戶實體類
讓用戶和賬戶的實體類能體現出來一對多的關系
3、建立兩個配置文件
用戶的配置文件
賬戶的配置文件
4、實現配置:
當我們查詢用戶時,可以同時得到用戶下所包含的賬戶信息
當我們查詢賬戶時,可以同時得到賬戶的所屬用戶信息
3.@Results注解用法總結:
MyBatis中使用@Results注解來映射查詢結果集到實體類屬性。
(1)@Results的基本用法。當數據庫字段名與實體類對應的屬性名不一致時,可以使用@Results映射來將其對應起來。column為數據庫字段名,porperty為實體類屬性名,jdbcType為數據庫字段數據類型,id為是否為主鍵。
@Select({"select id, name, class_id from my_student"}) @Results({ @Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true), @Result(column="name", property="name", jdbcType=JdbcType.VARCHAR), @Result(column="class_id", property="classId", jdbcType=JdbcType.INTEGER) }) List<Student> selectAll();
如上所示的數據庫字段名class_id與實體類屬性名classId,就通過這種方式建立了映射關系。
(2)@ResultMap的用法。當這段@Results代碼需要在多個方法用到時,為了提高代碼復用性,我們可以為這個@Results注解設置id,然后使用@ResultMap注解來復用這段代碼。
@Select({"select id, name, class_id from my_student"}) @Results(id="studentMap", value={ @Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true), @Result(column="class_id", property="classId", jdbcType=JdbcType.INTEGER) }) List<Student> selectAll(); @Select({"select id, name, class_id from my_student where id = #{id}"}) @ResultMap(value="studentMap") Student selectById(integer id);
(3)@One的用法。當我們需要通過查詢到的一個字段值作為參數,去執行另外一個方法來查詢關聯的內容,而且兩者是一對一關系時,可以使用@One注解來便捷的實現。比如當我們需要查詢學生信息以及其所屬班級信息時,需要以查詢到的class_id為參數,來執行ClassesMapper中的selectById方法,從而獲得學生所屬的班級信息。可以使用如下代碼。
@Select({"select id, name, class_id from my_student"}) @Results(id="studentMap", value={ @Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true), @Result(column="class_id", property="myClass", javaType=MyClass.class, one=@One(select="com.example.demo.mapper.MyClassMapper.selectById")) }) List<Student> selectAllAndClassMsg();
(4)@Many的用法。與@One類似,只不過如果使用@One查詢到的結果是多行,會拋出TooManyResultException異常,這種時候應該使用的是@Many注解,實現一對多的查詢。比如在需要查詢學生信息和每次考試的成績信息時。
@Select({"select id, name, class_id from my_student"}) @Results(id="studentMap", value={ @Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true), @Result(column="class_id", property="classId", jdbcType=JdbcType.INTEGER), @Result(column="id", property="gradeList", javaType=List.class, many=@Many(select="com.example.demo.mapper.GradeMapper.selectByStudentId")) }) List<Student> selectAllAndGrade();
參考文獻:https://blog.csdn.net/cherlshall/article/details/80950150
4.操作案例
(1)案例1
查詢所有查詢所有賬戶,及其用戶名和地址信息(邏輯為:賬戶表account-----》用戶信息表user)
<1>對數據庫表Account對應的實體類Account.java進行改造,加入User對象作為成員變量
package domain; import java.io.Serializable; /** * 數據庫的account表對應的實體類 */ public class Account implements Serializable { private Integer id; private Integer uid; private Double money; //從表實體應該包含一個主表實體的對象引用 private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getUid() { return uid; } public void setUid(Integer uid) { this.uid = uid; } public Double getMoney() { return money; } public void setMoney(Double money) { this.money = money; } @Override public String toString() { return "Account{" + "id=" + id + ", uid=" + uid + ", money=" + money + '}'; } }
<2>IAccountDao中添加findAll()方法
package dao; import domain.Account; import domain.AccountUser; import java.util.List; public interface IAccountDao { /** * 查詢所有查詢所有賬戶,及其用戶名和地址信息 * @return */ List<Account> findAll(); }
<3>映射關系配置
(1)IAccountDao.xml配置
ResultMap標簽基本作用:建立SQL查詢結果字段與實體屬性的映射關系信息
標簽屬性id和type的含義:
id:該resultMap的標志
type:返回值的類名
子標簽含義:
id:用於設置主鍵字段與領域模型屬性的映射關系,此處主鍵為ID,對應id。
result:用於設置普通字段與領域模型屬性的映射關系
association 為關聯關系,是實現一對一的關鍵
1. property 為javabean中容器對應字段名
2. javaType 指定關聯的類型,當使用select屬性時,無需指定關聯的類型
3. select 使用另一個select查詢封裝的結果
4. column 為數據庫中的列名,與select配合使用
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="dao.IAccountDao"> <resultMap id="accountUserMap" type="domain.Account"> <id property="id" column="aid"></id> <result property="uid" column="uid"></result> <result property="money" column="money"></result> <!--配置所外鍵所關聯表user的內容--> <association property="user" column="uid" javaType="domain.User"> <id property="id" column="id"></id> <result property="username" column="username"></result> <result property="birthday" column="birthday"></result> <result property="sex" column="sex"></result> <result property="address" column="address"></result> </association> </resultMap> <!-- 查詢所有查詢所有賬戶,及其用戶名和地址信息:方法1(更通用) --> <!--account a 給account表起一個別名--> <select id="findAll" resultMap="accountUserMap"> select u.*,a.id as aid,a.uid,a.money from account a , user u where u.id = a.uid; </select> </mapper>
注解配置:IAccountDao.java文件中進行如下配置
/** * 查詢所有查詢所有賬戶,及其用戶名和地址信息 * 注解中的第四個result的column為uid(外鍵),連接到user表,select調用dao.IUserDao.findById()方法 * @return */ @Select("select * from account") @Results(id="accountMap",value = { @Result(id=true,column = "id",property = "id"), @Result(column = "uid",property = "uid"), @Result(column = "money",property = "money"), @Result(column = "uid",property = "user",one=@One(select = "dao.IUserDao.findById",fetchType = FetchType.EAGER)) }) List<Account> findAll();
<4>測試代碼
package test; import dao.IAccountDao; import dao.IUserDao; import domain.Account; import domain.AccountUser; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.InputStream; import java.util.List; public class MybatisTest01 { private InputStream in; private SqlSession sqlSession; private IAccountDao accountDao; /** * 初始化MyBatis * @throws Exception */ public void initMyBatis() throws Exception{ //1.讀取配置文件 in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.創建SqlSessionFactory SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); //創建SqlSessionFactory的構建者builder SqlSessionFactory factory=builder.build(in); //利用構建者builder創建SqlSessionFactory //3.使用工廠生產SqlSession對象 sqlSession = factory.openSession(); //4.使用SqlSessions對象創建Dao接口的代理對象 accountDao = sqlSession.getMapper(IAccountDao.class); } /** * 釋放資源 * @throws Exception */ public void destroy() throws Exception{ sqlSession.commit();//提交事務 sqlSession.close(); in.close(); } /** * 查詢所有 * @throws Exception */ @Test public void testFindAll()throws Exception{ initMyBatis(); List<Account> accounts = accountDao.findAll(); for (Account account : accounts) { System.out.println(account); System.out.println(account.getUser()); } destroy(); } }
效果圖:
(2)案例2
查詢所有查詢用戶信息,及其擁有的賬戶信息(邏輯為:用戶信息表user-----》賬戶表account)
<1>對數據庫表user對應的實體類User.java進行改造,加入List<Account>作為成員變量
package domain; import java.io.Serializable; import java.util.Date; import java.util.List; /** * 數據庫表對應的實體類 */ public class User implements Serializable { //實體類的成員變量名稱應該與數據庫中的列名一致 private Integer id; private String username; private Date birthday; private String sex; private String address; private List<Account> accounts; public List<Account> getAccounts() { return accounts; } public void setAccounts(List<Account> accounts) { this.accounts = accounts; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", birthday=" + birthday + ", sex='" + sex + '\'' + ", address='" + address + '\'' + '}'; } }
<2>IUserDao中添加findAll()方法
package dao; import domain.User; import java.util.List; /** * */ public interface IUserDao { /** * 查詢所有 * @return */ List<User> findAll(); }
<3>IUserDao.xml
ResultMap標簽基本作用:建立SQL查詢結果字段與實體屬性的映射關系信息
標簽屬性id和type的含義:
id:該resultMap的標志
type:返回值的類名
子標簽含義:
id:用於設置主鍵字段與領域模型屬性的映射關系,此處主鍵為ID,對應id。
result:用於設置普通字段與領域模型屬性的映射關系
association 為關聯關系,是實現一對一的關鍵
1. property 為javabean中容器對應字段名
2. javaType 指定關聯的類型,當使用select屬性時,無需指定關聯的類型
3. select 使用另一個select查詢封裝的結果
4. column 為數據庫中的列名
collection 為關聯關系,是實現一對多的關鍵
1. property 為javabean中容器對應字段名
2. ofType 指定集合中元素的對象類型
3. select 使用另一個查詢封裝的結果
4. column 為數據庫中的列名,與select配合使用
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="dao.IUserDao"> <resultMap id="userAccountMap" type="domain.User"> <id property="id" column="id"></id> <result property="username" column="username"></result> <result property="birthday" column="birthday"></result> <result property="sex" column="sex"></result> <result property="address" column="address"></result> <!--配置user對象中accounts集合的映射--> <collection property="accounts" ofType="domain.Account"> <id property="id" column="aid"></id> <result property="uid" column="uid"></result> <result property="money" column="money"></result> </collection> </resultMap> <!-- 查詢所有 --> <select id="findAll" resultMap="userAccountMap"> select * from user u left outer join account a on u.id = a.uid </select> </mapper>
注解配置:IUserDao.java文件中進行如下配置
/** * 查詢所有 * @return */ @Select("select * from user") @Results(id="userMap",value = { @Result(id=true,column = "id",property = "id"), @Result(column = "username",property = "username"), @Result(column = "address",property = "address"), @Result(column = "sex",property = "sex"), @Result(column = "birthday",property = "birthday"), @Result(column = "id",property ="accounts",many =@Many(select = "dao.IAccountDao.findAccountById",fetchType = FetchType.EAGER)) }) List<User> findAll();
<4>測試代碼
package test; import dao.IAccountDao; import dao.IUserDao; import domain.Account; import domain.AccountUser; import domain.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.InputStream; import java.util.List; public class MybatisTest02_User { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; /** * 初始化MyBatis * @throws Exception */ public void initMyBatis() throws Exception{ //1.讀取配置文件 in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.創建SqlSessionFactory SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); //創建SqlSessionFactory的構建者builder SqlSessionFactory factory=builder.build(in); //利用構建者builder創建SqlSessionFactory //3.使用工廠生產SqlSession對象 sqlSession = factory.openSession(); //4.使用SqlSessions對象創建Dao接口的代理對象 userDao = sqlSession.getMapper(IUserDao.class); } /** * 釋放資源 * @throws Exception */ public void destroy() throws Exception{ sqlSession.commit();//提交事務 sqlSession.close(); in.close(); } /** * 查詢所有 * @throws Exception */ @Test public void testFindAll()throws Exception{ initMyBatis(); List<User> users = userDao.findAll(); for (User user : users) { System.out.println(user); System.out.println(user.getAccounts()); } destroy(); } }
效果圖:
4.Mybatis中的緩存
(1)什么是緩存
存在於內存中的臨時數據。
(2)為什么使用緩存
減少和數據庫的交互次數,提高執行效率。
(3)什么樣的數據能使用緩存,什么樣的數據不能使用
<1>適用於緩存:
經常查詢並且不經常改變的。
數據的正確與否對最終結果影響不大的。
<2>不適用於緩存:
經常改變的數據
數據的正確與否對最終結果影響很大的。
例如:商品的庫存,銀行的匯率,股市的牌價。
5.Mybatis中的一級緩存和二級緩存
(1)一級緩存:
它指的是Mybatis中SqlSession對象的緩存。
當我們執行查詢之后,查詢的結果會同時存入到SqlSession為我們提供一塊區域中。
該區域的結構是一個Map。當我們再次查詢同樣的數據,mybatis會先去sqlsession中
查詢是否有,有的話直接拿出來用。
當SqlSession對象消失時,mybatis的一級緩存也就消失了。
(2)二級緩存:
它指的是Mybatis中SqlSessionFactory對象的緩存。由同一個SqlSessionFactory對象創建的SqlSession共享其緩存。
二級緩存的使用步驟:
第一步:讓Mybatis框架支持二級緩存(在SqlMapConfig.xml中配置)
第二步:讓當前的映射文件支持二級緩存(在IUserDao.xml中配置)
第三步:讓當前的操作支持二級緩存(在select標簽中配置)