MyBatis--單表的CURD操作(原始dao方式)
CURD操作,即指對數據庫中實體對象的增create、改Update、查Read、刪Delete操作
一、自定義Dao接口實現類
搭建環境
Dao接口:
public interface IStudentDao {
//插入
void insertStudent(Student student);
void insertStudentCacheId(Student student);
//刪改
void deleteStudentById(int id);
void updateStudent(Student student);
//查詢所有
List<Student> selectAllStudents();
Map<String, Object> selectAllStudentsMap();
//查詢指定學生
Student selectStudentById(int id);
Student selectStudentByMap(Map<String, Object> map);
//根據姓名查詢
List<Student> selectStudentsByName(String name);
}
Dao實現類:
public class StudentDaoImpl implements IStudentDao {
private SqlSession sqlSession;
@Override
public void insertStudent(Student student) {
try {
sqlSession = MyBatisUtils.getSqlSession();
sqlSession.insert("insertStudent",student);
sqlSession.commit();
} finally{
if(sqlSession!=null){
sqlSession.close();
}
}
}
}
測試:
public class MyTest {
private IStudentDao dao;
@Before
public void before(){
dao = new StudentDaoImpl();
}
}
1.單純插入數據
映射文件:
<insert id="insertStudent" parameterType="com.hcx.beans.Student">
insert into
student(name,age,score)
values(#{name},#{age},#{score})
</insert>
- id:該sql語句的唯一標識,java代碼中要使用該標識。
- "#{}":對指定參數類型屬性值的引用。其底層是通過反射機制,調用student類相關屬相的get方法來獲取值得。因為底層使用的是反射,所以這里使用的是類的屬性名,而非表的字段名。
dao實現類:
@Override
public void insertStudent(Student student) {
try {
sqlSession = MyBatisUtils.getSqlSession();
sqlSession.insert("insertStudent",student);
sqlSession.commit();
} finally{
if(sqlSession!=null){
sqlSession.close();
}
}
}
注意:執行完對DB的修改操作,必須要做Sqlsession的提交。否則,修改將無法同步到DB中。因為使用無參的openSession()方法已經將事務自動提交功能給關閉了。
測試類:
@Test
public void test01(){
Student student = new Student("張三",23,99.8);
dao.insertStudent(student);
}
2.插入后用新id初始化被插入對象
mysql中在插入語句后執行如下語句,則會輸出新插入記錄的id
insert into student(name,age,score) values('王五',25,95.5);
select @@identity;
映射文件中的<insert/>標簽中,有一個子標簽<selectKey/>用于獲取新插入記錄的主鍵值。
1.以下兩種寫法均可以完成“使用新插入記錄的主鍵值初始化被插入的對象”的功能:
方式一:
<insert id="insertStudentCatchId" parameterType="com.hcx.beans.Student">
insert into student(name,age,score)
values(#{name},#{age},#{score})
<selectKey resultType="int" keyProperty="id" order="AFTER">
select @@identity
</selectKey>
</insert>
方式二:
<insert id="insertStudentCatchId2" parameterType="com.hcx.beans.Student">
insert into student(name,age,score)
values(#{name},#{age},#{score})
<selectKey resultType="int" keyProperty="id" order="AFTER">
select last_insert_id();
</selectKey>
</insert>
- resultType:指出獲取的主鍵的類型
- keyProperty:指出主鍵在java類中對應的屬性名。此處會將獲取的主鍵值直接封裝到被插入的student對象中,即dao中insert()方法的第二個參數對象中。
- order:指出id的生成相對于insert語句的執行是在前還是在后。MySql數據庫表中的id,均是先執行insert語句,而后生成id,所以需要設置為after;oracle數據庫表中的id,則是在insert執行之前先生成,所以需要設置為before。當前的MyBatis版本,不指定order屬性,則會根據所用DBMS,自動選擇其值。
2.Dao實現類
@Override
public void insertStudentCacheId(Student student) {
try {
sqlSession = MyBatisUtils.getSqlSession();
sqlSession.insert("insesrtStudentCatchId",student);
sqlSession.commit();
}finally{
if(sqlSession != null) {
sqlSession.close();
}
}
}
3.測試類:
//插入后用新id初始化被插入對象
@Test
public void test02(){
Student student = new Student("張三",23,99.8);
System.out.println("插入前student="+student);
dao.insertStudentCatchId(student);
System.out.println("插入后student="+student);
}
3.刪除數據
1.映射文件:
<delete id="deleteStudentById">
delete from student where id=#{id}
</delete>
注意:這里的動態參數id所賦值為#{id}。這個#{}表示這就是個占位符,代表delete()方法的第二個參數。#{}中可以放任意值,無需要與delete方法的第二個參數值相同。
2.dao實現類
@Override
public void deleteStudentById(int id) {
try {
sqlSession = MyBatisUtils.getSqlSession();
sqlSession.insert("deleteStudentById",id);
sqlSession.commit();
}finally{
if(sqlSession != null) {
sqlSession.close();
}
}
}
3.測試類
@Test
public void test03(){
dao.deleteStudentById(3);
}
4.修改數據
1.映射文件:
<update id="updateStudnet">
update student
set name=#{name},age=#{age},score=#{score}
where id=#{id}
</update>
注意,這里的#{}中,必須要填寫update()方法所傳第二個參數student對象的屬性名稱,不能隨意填寫。
2.dao實現類
@Override
public void updateStudent(Student student) {
try {
sqlSession = MyBatisUtils.getSqlSession();
sqlSession.update("updateStudnet",student);
sqlSession.commit();
}finally{
if(sqlSession != null) {
sqlSession.close();
}
}
}
3.測試類
@Test
public void test04(){
Student student = new Student("得的",26,96.6);
student.setId(5);
dao.updateStudent(student);
}
5.查詢所有對象-返回list
1.映射文件
<select id="selectAllStudents" resultType="com.hcx.beans.Student">
select * from student
</select>
注意,resultType屬性并非指查詢結果最后的類型,而是每查出DB中的一條記錄,將該記錄封裝成為的對象的類型。
2.dao實現類
完成dao實現類的selectAllStudent()方法。使用Sqlsession的selectList()方法完成查詢操作。該方法會將查詢出的每條記錄封裝為指定類型對象后,再將最后的結果集封裝為list返回。
方法原型為:List selectList(String statement)
statement:映射文件中配置的SQL語句的id。
@Override
public List<Student> selectAllStudents() {
List<Student> students = null;
try {
sqlSession = MyBatisUtils.getSqlSession();
students = sqlSession.selectList("selectAllStudents");
//sqlSession.commit();
}finally{
if(sqlSession != null) {
sqlSession.close();
}
}
return students;
}
注意:在寫查詢時,由于不是對DB中的數據進行修改,所以無需進行Sqlsession的提交。但最終Sqlsession對象還是需要關閉的。
3.測試類
@Test
public void test05(){
List<Student> students = dao.selectAllStudents();
for (Student student : students) {
System.out.println(student);
}
}
6.查詢所有對象-返回Map
1.映射文件
<select id="selectAllStudents" resultType="com.hcx.beans.Student">
select * from student
</select>
2.dao實現類
完成dao實現類的selectStudentMap()方法。使用Sqlsession的selectMap()方法完成查詢操作。該查詢方法會將查詢出的每條記錄先封裝為指定對象,然后再將該對象作為value,將該對象的指定屬性所對應的字段名作為key封裝為一個map對象。
方法原型:Map<Object,Object> selectMap(String statement,String mapKey)
- statement:映射文件中配置的sql語句的id
- mapkey:查詢出的map所要使用的key。這個key為數據表的字段名。查詢出的結果是一個map,每行記錄將會對應一個Map.entry對象,該對象的key為指定字段的值,value為記錄數據所封裝的對象
代碼:
@Override
public Map<String, Object> selectAllStudentsMap() {
Map<String, Student> studentsMap = null;
try {
sqlSession = MyBatisUtils.getSqlSession();
studentsMap = sqlSession.selectMap("selectAllStudents", "name"); //name:指定所形成map的key
} finally{
if(sqlSession!=null){
sqlSession.close();
}
}
return null;
}
3.測試類
@Test
public void test06(){
Map<String, Student> map = dao.selectAllStudentsMap();
Student student = map.get("張三");
System.out.println(student);
}
注意:若指定的作為key的屬性值在DB中并不唯一,則后面的記錄值會覆蓋掉前面的值。即指定key的value值,一定是DB中該同名屬性的最后一條記錄值。
7.查詢單個對象
1.映射文件
<select id="selectStudnetById" resultType="com.hcx.beans.Student">
select * from student where id=#{id}
</select>
2.dao實現類
使用Sqlsession的selectOne()方法。其會將查詢的結果記錄封裝為一個指定類型的對象。
方法原型:Object selectOne(String statement,Object parameter)
- statement:映射文件中配置的SQL語句的id
- parameter:查詢條件中動態參數的值
代碼:
@Override
public Student selectStudentById(int id) {
Student student = null;
try {
sqlSession = MyBatisUtils.getSqlSession();
student = sqlSession.selectOne("selectStudentById",id);
sqlSession.commit();
}finally{
if(sqlSession != null) {
sqlSession.close();
}
}
return student;
}
3.測試類
@Test
public void test07(){
Student student = dao.selectStudentById(3);
System.out.println(student);
}
8.模糊查詢
1.映射文件
寫法一:
<select id="selectStudnetByName" resultType="com.hcx.beans.Student">
select * from student
where name like concat('%',#{name},'%')
</select>
在進行模糊查詢時,需要進行字符串的拼接。sql中的字符串的拼接使用的是函數concat(arg1,arg2,...)。注意不能使用java中的字符串連接符+。
寫法二:
<select id="selectStudnetByName" resultType="com.hcx.beans.Student">
select * from student
where name like '%' #{name} '%'
</select>
以上兩種形式是等效的。都是以動態參數的形式出現在sql語句中的。
寫法三:
還可使用如下方式,需要注意,只能使用${}中只能使用value,不能使用其他:
<select id="selectStudnetByName" resultType="com.hcx.beans.Student">
select * from student
where name like '%${value}%'
</select>
這種方式是純粹的字符串拼接,直接將參數拼接到了sql語句中,所以可能會發生sql注入
2.dao實現類
@Override
public List<Student> selectStudentsByName(String name) {
List<Student> students = null;
try {
sqlSession = MyBatisUtils.getSqlSession();
students = sqlSession.selectOne("selectStudentByName",name);
sqlSession.commit();
}finally{
if(sqlSession != null) {
sqlSession.close();
}
}
return students;
}
3.測試類
@Test
public void test08(){
List<Student> students = dao.selectStudentsByName("張");
for (Student student : students) {
System.out.println(student);
}
}
$與#的區別:
#為占位符,$為字符串拼接符
字符串拼接是將參數值以硬編碼的方式直接拼接到了sql語句中。
字符串拼接就會引發兩個問題:
sql注入問題與沒有使用預編譯所導致的執行效率低下問題
應用場景:
一般情況下,動態參數的值是由用戶輸入的,則不能使用拼接符$,因為有可能會出現sql注入;若動態參數的值是由系統計算生成的,則可以使用拼接符$。但這樣雖然不存在sql注入的風險,但仍存在執行效率問題。
9.根據map進行查詢
mapper中sql語句的動態參數也可以是map的key
1.測試類
map中存放兩個“鍵-值”對,即有兩個key
@Test
public void test09(){
Student student = new Student();
student.setId(5);
Map<String, Object> map = new HashMap<String,Object>();
map.put("studentId", 2);
map.put("student", student);
student = dao.selectStudentByMap(map);
System.out.println(student);
}
2.映射文件
<select id="selectStudentByMap" resultType="com.hcx.beans.Student">
select * from student
where id=#{studentId}
</select>
<select id="selectStudentByMap2" resultType="com.hcx.beans.Student">
select * from student
where id=#{student.id}
</select>
3.dao實現類
@Override
public Student selectStudentByMap(Map<String, Object> map) {
Student student = null;
try {
sqlSession = MyBatisUtils.getSqlSession();
student = sqlSession.selectOne("selectStudentByMap",map);
sqlSession.commit();
}finally{
if(sqlSession != null) {
sqlSession.close();
}
}
return student;
}
二、屬性名與查詢字段名不相同
resultType可以將查詢結果直接映射為實體Bean對象的條件是,sql查詢的字段名與實體Bean的屬性名一致。因為在將查詢結果轉換為指定類型對象時,系統自動將查詢結果字段名稱作為對象的屬性名,通過反射機制完成對象的創建。
當sql查詢結果的字段名和實體Bean的屬性名不一致時,將無法創建出需要類型的對象。此時有兩種解決方案:查詢字段使用別名或使用結果映射resultMap
1.修改student表
2.dao接口
public interface IStudentDao {
Student selectStudentById(int id);
}
3.測試類
public class MyTest {
@Test
public void test1(){
IStudentDao dao = new StudentDaoImpl();
Student student = dao.selectStudentById(3);
System.out.println(student);
}
}
4.dao實現類
public class StudentDaoImpl implements IStudentDao {
@Override
public Student selectStudentById(int id) {
SqlSession sqlSession = null;
Student student = null;
try {
sqlSession = MyBatisUtils.getSqlSession();
student = sqlSession.selectOne("selectById2",id);
} finally{
if(sqlSession!=null){
sqlSession.close();
}
}
}
}
解決方式一:查詢字段使用別名
雖然屬性名稱與表中的字段名稱不一樣,但可以為查詢結果的字段名稱賦予別名,讓別名與實體Bean的屬性相同。這樣框架也可以根據查詢結果利用反射機制將對象創建。
在映射文件mapper中添加如下映射。注意,由于表的score字段名與student類的屬性名相同,所以這里無需使用別名。
<select id="selectStudentById" resultType="Student">
select tid id,tname name,tage,age,score
from student where tid=#{tid}
</select>
解決方式二:使用結果映射resultMap
可以使用結果映射resultMap來建立映射關系,完成由字段到屬性的映射,達到將查詢結果封裝為對象的目的。resultMap是對resultType的增強。
<resultMap type="Student" id="studentMapper">
<id column="tid" property="id"/>
<result column="tname" property="name"/>
<result column="tage" property="age"/>
</resultMap>
<select id="selectStudentById" resultMap="studentMapper">
select tid, tname, tage, score
from student where tid=#{tid}
</select>
<resultMap/>標簽中定義了由type指定的類的屬性名到表中字段名稱的映射關系。根據這個映射關系,框架利用反射機制創建相應對象。
- type:指定要映射的實體類
- id:指定該resultMap映射關系的名稱
- <id>標簽:id的字段名column與實體類的屬性property間的映射關系
- <result>標簽:id以外其他字段名column與實體類的屬性property間的映射關系
如果字段名與實體類的屬性名相同的情況,可以不寫入<resultMap/>中。