MyBatis
parameterType和resultType
- parameterType:指定輸入?yún)?shù)的java類型,可以填寫別名或Java類的全限定名
- resultType:指定輸出結(jié)果的java類型风皿,可以填寫別名或Java類的全限定名
#{}和${}
-
{}:相當(dāng)于預(yù)處理中的占位符?变丧。
{}里面的參數(shù)表示接收java輸入?yún)?shù)的名稱。
{}可以接受HashMap绢掰、POJO類型的參數(shù)痒蓬。
當(dāng)接受簡單類型的參數(shù)時,#{}里面可以是value滴劲,也可以是其他攻晒。
{}可以防止SQL注入。
-
${}:相當(dāng)于拼接SQL串哑芹,對傳入的值不做任何解釋的原樣輸出。
${}會引起SQL注入捕透,所以要謹(jǐn)慎使用聪姿。
${}可以接受HashMap碴萧、POJO類型的參數(shù)。
當(dāng)接受簡單類型的參數(shù)時末购,${}里面只能是value破喻。
selectOne和selectList
- selectOne:只能查詢0或1條記錄,大于1條記錄的話盟榴,會報錯:
- selectList:可以查詢0或N條記錄
Mybatis基本步驟
-
添加依賴包
- 添加log4j.properties(mybatis日志包是log4j)
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
> 日志級別在開發(fā)階段設(shè)置成DEBUG曹质,在生產(chǎn)階段設(shè)置成INFO或者ERROR
- 書寫全局的配置文件
SqlMapConfig.xml:在src目錄下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--此時識別的是src目錄下的文件-->
<properties resource="db.properties"/>
<!--配置別名-->
<typeAliases>
<!--方式一-->
<!--<typeAlias type="com.zengqiang.model.User" alias="user"></typeAlias>-->
<!--方式二-->
<!--指定包名,別名就是類名并且第一個字母小寫 例如 User 別名就是user-->
<package name="com.zengqiang.model"></package>
<package name="com.zengqiang.vo"></package>
</typeAliases>
<!-- 配置mybatis的環(huán)境信息 -->
<environments default="development">
<environment id="development">
<!-- 配置JDBC事務(wù)控制擎场,由mybatis進行管理 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置數(shù)據(jù)源羽德,采用dbcp連接池 -->
<dataSource type="POOLED">
<property name="driver" value="${driverClass}"/>
<property name="url" value="${url}"/>
<property name="username" value="${name}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--告訴mybatis加載映射文件-->
<mappers>
<!--<mapper resource="com/zengqiang/sqlmap/User.xml"></mapper>-->
<!--第一種:寫映射文件的名字-->
<!--<mapper resource="com/zengqiang/mapper/UserMapper.xml"></mapper>-->
<!--第二種:寫類名,此種方法一定要有個映射文件與之對應(yīng)
如果沒有,那么在UserMapper接口中要聲明注解-->
<!--<mapper class="com.zengqiang.mapper.UserMapper"></mapper>-->
<!--第三種:可以寫包名,很方便-->
<package name="com.zengqiang.mapper"></package>
</mappers>
</configuration>
- 數(shù)據(jù)庫信息文件
db.properties:在src目錄下
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatisday1?useUnicode=true&characterEncoding=utf8
name=root
password=123456
簡化案例(不分層迅办,只使用)
- 在com.zengqiang包下創(chuàng)建文件夾sqlmap(名稱隨意),然后創(chuàng)建User.xml文件
- 在com.zengqiang包下創(chuàng)建文件夾dao(名稱隨意),然后創(chuàng)建User.java文件
User.java
public class User implements Serializable {
private int id;
private String username;// 用戶姓名
private String sex;// 性別
private Date birthday;// 生日
private String address;// 地址
public User() {
}
public User(String username, String sex, Date birthday, String address) {
this.username = username;
this.sex = sex;
this.birthday = birthday;
this.address = address;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", sex=" + sex
+ ", birthday=" + birthday + ", address=" + address + "]";
}
}
User.xml
<?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">
<!--namespace:命名空間宅静,值一般按照對應(yīng)statement的類名即可,沒有必然對應(yīng)關(guān)系-->
<mapper namespace="user">
<!--根據(jù)id查詢-->
<!--
[id]:statement的id站欺,要求在命名空間內(nèi)唯一
[parameterType]:入?yún)⒌膉ava類型
[resultType]:查詢出的單條結(jié)果集對應(yīng)的java類型
[#{}]: 表示一個占位符?
[#{id}]:表示該占位符待接收參數(shù)的名稱為id姨夹。
注意:如果參數(shù)為簡單類型時,#{}里面的參數(shù)名稱可以是任意定義
-->
<select id="findUserById" parameterType="int" resultType="com.zengqiang.model.User">
SELECT * FROM user WHERE id = #{id}
</select>
<!--
[${}]:表示拼接SQL字符串
[${value}]:表示要拼接的是簡單類型參數(shù)矾策。
注意:
簡單類型:int,byte,... string
1磷账、如果參數(shù)為簡單類型時,${}里面的參數(shù)名稱必須為value
2贾虽、${}會引起SQL注入逃糟,一般情況下不推薦使用。但是有些場景必須使用${}榄鉴,比如order by ${colname}
-->
<select id="findUserByName" parameterType="String" resultType="com.zengqiang.model.User">
SELECT * FROM user WHERE username LIKE '%${value}%'
-- SELECT * FROM user WHERE username LIKE '%"#{value}"%'
</select>
<!--插入數(shù)據(jù)
這里的占位是寫模型的屬性
-->
<insert id="insertUser" parameterType="com.zengqiang.model.User">
INSERT INTO user (username,sex,birthday,address)
VALUE (#{username},#{sex},#{birthday},#{address});
</insert>
<!--刪除-->
<delete id="deleteUser" parameterType="int">
DELETE FROM user where id = #{id};
</delete>
<!--更新-->
<update id="updateUser" parameterType="com.zengqiang.model.User">
UPDATE user SET address = #{address},sex = #{sex}
WHERE id = #{id}
</update>
<!--插入時自動返回主鍵id-->
<insert id="insertUser2" parameterType="com.zengqiang.model.User">
<!--
[selectKey標(biāo)簽]:通過select查詢來生成主鍵
[keyProperty]:指定存放生成主鍵的屬性
[resultType]:生成主鍵所對應(yīng)的Java類型
[order]:指定該查詢主鍵SQL語句的執(zhí)行順序履磨,相對于insert語句
[last_insert_id]:MySQL的函數(shù),要配合insert語句一起使用
下面也可以:SELECT UUID()庆尘,然后返回mysql自增的UUID
-->
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO user (username,sex,birthday,address)
VALUE (#{username},#{sex},#{birthday},#{address});
</insert>
</mapper>
單元測試類:注意增刪改都需要提交事務(wù)(commit)剃诅,然后close,但是查找直接close即可
public class Demo{
SqlSession session;
@Before
public void before() throws IOException {
System.out.println("before.....獲取session");
//a)讀取配置文件驶忌;
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
//b)通過SqlSessionFactoryBuilder創(chuàng)建SqlSessionFactory會話工廠矛辕。
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
// c)通過SqlSessionFactory創(chuàng)建SqlSession。
session = sessionFactory.openSession();
}
@After
public void after(){
System.out.println("after.....關(guān)閉session");
// 關(guān)閉SqlSession付魔。
session.close();
}
/**
* 查詢:一條記錄和多條記錄
* @throws IOException
*/
@Test
public void test1() throws IOException {
//查詢一條結(jié)果,其中findUserById對應(yīng)User.xml中的statement的id
User user = session.selectOne("findUserById",10);
System.out.println(user);
//查詢多條結(jié)果
List<User> users = session.selectList("findUserByName","張");
System.out.println(users);
}
/**
* 插入數(shù)據(jù)
* @throws IOException
*/
@Test
public void test2() throws IOException {
User user = new User("zengqiang04","2",new Date(),"廣州");
int affectRow = session.insert("insertUser",user);
session.commit();//事務(wù)
System.out.println("受影響的行數(shù):" + affectRow);
}
//刪除用戶
@Test
public void test3() throws IOException {
int affectRow = session.delete("deleteUser",27);
session.commit();//事務(wù)
System.out.println("受影響的行數(shù):" + affectRow);
}
//更新用戶
@Test
public void test4() throws IOException {
User user = new User();
user.setId(32);
user.setSex("男");
user.setAddress("深圳");
int affectRow = session.update("updateUser",user);
session.commit();//事務(wù)
System.out.println("受影響的行數(shù):" + affectRow);
}
/**
* 插入后聊品,往模型里設(shè)置id
* @throws IOException
*/
@Test
public void test5() throws IOException {
User user = new User("zengqiang05","2",new Date(),"廣州");
int affectRow = session.insert("insertUser2",user);
session.commit();//事務(wù)
System.out.println("受影響的行數(shù):" + affectRow);
System.out.println("用戶的ID:" + user.getId());
}
}
分Dao層(手寫實現(xiàn)類,不常用)
- 在com.zengqiang包下建立dao文件夾
- 在該文件夾下面創(chuàng)建UserDao接口
- 在該文件夾下面創(chuàng)建UserDaoImpl.java文件
public interface UserDao {
/**
* 保存一個用戶
* @param user
*/
public void save(User user);
public User findUserById(int id);
}
public class UserDaoImpl implements UserDao {
private SqlSessionFactory ssf;
public UserDaoImpl() {
}
public UserDaoImpl(SqlSessionFactory ssf) {
this.ssf = ssf;
}
@Override
public void save(User user) {
//獲取session
SqlSession session = ssf.openSession();
//插入數(shù)據(jù)
session.insert("insertUser",user);
session.commit();
session.close();
}
@Override
public User findUserById(int id) {
//獲取session
SqlSession session = ssf.openSession();
//插入數(shù)據(jù)
User user = session.selectOne("findUserById",id);
session.close();
return user;
}
}
說明:該種方式也需要有個User.xml文件
單元測試類:
public class Demo {
SqlSessionFactory sessionFactory;
@Before
public void before() throws IOException {
System.out.println("before.....獲取session");
// a)讀取配置文件几苍;
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
//b)通過SqlSessionFactoryBuilder創(chuàng)建SqlSessionFactory會話工廠翻屈。
sessionFactory = new SqlSessionFactoryBuilder().build(is);
}
@Test
public void test1() throws IOException {
//調(diào)用dao
//1.創(chuàng)建dao
UserDao userDao = new UserDaoImpl(sessionFactory);
User user1 = userDao.findUserById(1);
System.out.println(user1);
User user2 = new User("xxx","x",new Date(),"xx");
userDao.save(user2);
}
}
Spring自動生成實現(xiàn)類(常用)
Mapper代理的開發(fā)方式,程序員只需要編寫mapper接口(相當(dāng)于dao接口)即可妻坝。Mybatis會自動的為mapper接口生成動態(tài)代理實現(xiàn)類伸眶。
不過要實現(xiàn)mapper代理的開發(fā)方式惊窖,需要遵循一些開發(fā)規(guī)范
- mapper接口的全限定名要和mapper映射文件的namespace的值相同。
- mapper接口的方法名稱要和mapper映射文件中的statement的id相同厘贼;
- mapper接口的方法參數(shù)只能有一個界酒,且類型要和mapper映射文件中statement的parameterType的值保持一致。
- mapper接口的返回值類型要和mapper映射文件中statement的resultType值或resultMap中的type值保持一致嘴秸;
步驟
- 在com.zengqiang包下新建mapper文件夾(名字必須固定)
- 該包下創(chuàng)建UserMapper(命名一般對應(yīng)dao下面的文件毁欣,此時對應(yīng)User.java)
- 創(chuàng)建UserMapper.xml
UserMapper.java
public interface UserMapper {
/**
*
* @param user
* @return 受影響的行數(shù)
*/
//此處對應(yīng)SqlMapConfig.xml種說的,如果不添加UserMapper.xml文件則通過注解也可岳掐,但是不常用
//@Insert("INSERT INTO user (username,sex,birthday,address) VALUE (#{username},#{sex},#{birthday},#{address})")
public int save(User user);
//@Select("SELECT * FROM user WHERE id = #{id}")
public User findUserById(int id);
public List<User> findUserByUserQueryVo(UserQueryVO vo);
public List<User> findUserByMap(Map<String,Object> map);
}
UserMapper.xml
<?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="com.zengqiang.mapper.UserMapper">
<insert id="save" parameterType="user">
INSERT INTO user (username,sex,birthday,address)
VALUE (#{username},#{sex},#{birthday},#{address})
</insert>
<select id="findUserById" parameterType="int" resultType="user">
SELECT * FROM user WHERE id = #{id}
</select>
<!--通過包裝類查詢用戶-->
<select id="findUserByUserQueryVo" parameterType="userQueryVO" resultType="user">
SELECT u.* FROM user u WHERE u.id = #{user.id}
</select>
<!--通過Map查詢數(shù)據(jù)-->
<select id="findUserByMap" parameterType="hashmap" resultType="user">
SELECT u.* FROM user u WHERE username LIKE '%${username}%' AND sex = #{sex}
</select>
</mapper>
> 說明:此時之所以parameterType和resultType等都可以直接簡寫凭疮,是因為在SqlMapConfig.xml種配置了別名
<!--配置別名-->
<typeAliases>
<!--方式一-->
<!--<typeAlias type="com.zengqiang.model.User" alias="user"></typeAlias>-->
<!--方式二-->
<!--指定包名,別名就是類名并且第一個字母小寫 例如 User 別名就是user-->
<package name="com.zengqiang.model"></package>
<package name="com.zengqiang.vo"></package>
</typeAliases>
基本測試
public class Demo {
SqlSession session;
@Before
public void before() throws IOException {
System.out.println("before.....獲取session");
// a)讀取配置文件岩四;
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
//b)通過SqlSessionFactoryBuilder創(chuàng)建SqlSessionFactory會話工廠哭尝。
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
session = sessionFactory.openSession();
}
@After
public void after(){
session.close();
}
@Test
public void test1() throws IOException {
//返回的是代理對象,所以可以轉(zhuǎn)換成對應(yīng)的Mapper
UserMapper userMapper = session.getMapper(UserMapper.class);
//獲取數(shù)據(jù)
System.out.println(userMapper.findUserById(1));
//保存
/*User user2 = new User("xxx","x",new Date(),"xx");
userMapper.save(user2);
session.commit();*/
}
}
包裝類和Map查詢
public class Demo05 {
SqlSession session;
@Before
public void before() throws IOException {
System.out.println("before.....獲取session");
// a)讀取配置文件剖煌;
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
//b)通過SqlSessionFactoryBuilder創(chuàng)建SqlSessionFactory會話工廠材鹦。
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
session = sessionFactory.openSession();
}
@After
public void after(){
session.close();
}
@Test
public void test1() throws IOException {
UserMapper userMapper = session.getMapper(UserMapper.class);
//System.out.println(obj.getClass());
//通過模型的包裝類來查詢用戶
UserQueryVO query = new UserQueryVO();
User user = new User();
user.setId(1);
query.setUser(user);
Order order = new Order();
order.setNumber("100111");
query.setOrder(order);
List<User> list = userMapper.findUserByUserQueryVo(query);
System.out.println(list);
}
@Test
public void test2() throws IOException {
UserMapper userMapper = session.getMapper(UserMapper.class);
//查詢條件
Map<String,Object> map = new HashMap<String,Object>();
map.put("username","張");
map.put("sex","2");
List<User> list = userMapper.findUserByMap(map);
System.out.println(list);
}
}
包裝類:
public class UserQueryVO {
private User user;
private Order order;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
輸出映射 resultType/resultMap
resultType
- 使用resultType進行結(jié)果映射時,查詢的列名和映射的pojo屬性名完全一致耕姊,該列才能映射成功桶唐。
- 如果查詢的列名和映射的pojo屬性名全部不一致,則不會創(chuàng)建pojo對象茉兰。
- 如果查詢的列名和映射的pojo屬性名有一個一致尤泽,就會創(chuàng)建pojo對象。
輸出單個pojo對象和pojo列表時规脸,mapper映射文件中的resultType的類型是一樣的坯约,mapper接口的方法返回值不同。
同樣的mapper映射文件莫鸭,返回單個對象和對象列表時闹丐,mapper接口在生成動態(tài)代理的時候,會根據(jù)返回值的類型被因,決定調(diào)用selectOne方法還是selectList方法卿拴。
resultMap
如果查詢出來的列名和屬性名不一致,通過定義一個resultMap將列名和pojo屬性名之間作一個映射關(guān)系梨与。
- 定義resultMap
- 使用resultMap作為statement的輸出映射類型
if和where
- If標(biāo)簽:作為判斷入?yún)硎褂玫亩榛ǎ绻蠗l件,則把if標(biāo)簽體內(nèi)的SQL拼接上粥鞋。
注意:用if進行判斷是否為空時缘挽,不僅要判斷null,也要判斷空字符串‘’; - Where標(biāo)簽:會去掉條件中的第一個and符號壕曼。
foreach遍歷
例如:SELECT * FROM
user
where id in (31,32,33);
SQL片斷
Mybatis提供了SQL片段的功能杠袱,可以提高SQL的可重用性
<?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="com.zengqiang.mapper.UserMapper">
<!--1 設(shè)置返回數(shù)據(jù)為基本類型,int,double,long,string....-->
<!-- 查詢用戶的個數(shù) -->
<select id="findUserCount" parameterType="userQueryVO" resultType="int">
SELECT COUNT(*) FROM user WHERE sex = #{user.sex}
</select>
<!--2.設(shè)置返回數(shù)據(jù)為resultMap -->
<resultMap id="userResultMap" type="user">
<id property="id" column="id_"></id>
<result property="username" column="username_"></result>
<result property="sex" column="sex_"></result>
<result property="birthday" column="birthday_"></result>
<result property="address" column="address_"></result>
</resultMap>
<select id="findUserByIdResultMap" parameterType="int" resultMap="userResultMap">
SELECT
id id_,
username username_,
sex sex_,
birthday birthday_,
address address_
FROM user WHERE id = #{id}
</select>
<!-- 3.if和where的使用-->
<!--聲明sql片段,提高重用性-->
<sql id="select_user_where">
<if test="user != null">
<if test="user.sex != null and user.sex != ''">
sex = #{user.sex}
</if>
<if test="user.username != null and user.username != ''">
and username LIKE '%${user.username}%'
</if>
<if test="user.address != null and user.address != ''">
and address LIKE '%${user.address}%'
</if>
</if>
</sql>
<select id="findUserList" parameterType="userQueryVO" resultType="user">
/*性別和名字*/
SELECT * FROM user
<where>
-- 引用sql片段
<include refid="select_user_where"/>
</where>
</select>
<!-- 4.foreach使用講解-->
<select id="findUserByIds" parameterType="userQueryVO" resultType="user">
<!--性別和名字 SELECT * FROM user WHERE id in (1,2,3) -->
SELECT * FROM user
<where>
<if test="ids != null and ids.size > 0">
<!--
collection:集合,寫集合屬性
item:遍歷接收變量
open:遍歷開始
close:遍歷結(jié)束
separator:拼接格式
for(Integer id : ids){
}
-->
<foreach collection="ids" item="id" open="id in(" close=")" separator=",">
${id}
</foreach>
</if>
</where>
</select>
<!-- 5.參數(shù)是數(shù)組(其實就是List)
如果參數(shù)是數(shù)組的話窝稿,parameterType可以寫全名【java.util.List】,也可以寫別名,
另外注意:
遍歷或者判斷的時候凿掂,都用list變量
-->
<select id="findUserByIds2" parameterType="list" resultType="user">
<!--性別和名字 SELECT * FROM user WHERE id in (1,2,3) -->
SELECT * FROM user
<where>
<if test="list != null and list.size > 0">
<foreach collection="list" item="id" open="id in(" close=")" separator=",">
${id}
</foreach>
</if>
</where>
</select>
</mapper>
UserMapper.java
public interface UserMapper {
/**
* 返回用戶的個數(shù)
* @param vo
* @return
*/
public int findUserCount(UserQueryVO vo);
public User findUserByIdResultMap(int userId);
/**
* 講解mybatis的if和where使用
* @return
*/
public List<User> findUserList(UserQueryVO vo);
/*查找多個id的用戶數(shù)據(jù)*/
public List<User> findUserByIds(UserQueryVO vo);
public List<User> findUserByIds2(List<Integer> ids);
/**
* 查詢用戶信息及用戶購買的商品信息
*/
public List<User> findUserAndOrderInfo();
public User findUserAndOrderInfo(int userId);
}
包裝類:pojo
public class UserQueryVO {
private User user;
private List<Integer> ids;
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
private Orders order;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Orders getOrder() {
return order;
}
public void setOrder(Orders order) {
this.order = order;
}
}