1.Mybatis: (是一款优秀的持久层框架,它支持 SQL、存储过程以及高级映射)
注解: 使用注解,不需要有XML配置文件,需要将代码和SQL写在一起
XML: 把SQL语句放到XML文件中
2.Mybatis环境搭建:
导包:
创建jdbc.properties 和 Mybatis主配置文件:
<?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>
<!-- 设置别名 -->
<!-- <typeAlias type="类的全路径" alias="别名"/> -->
<!--
typeAliases>
<typeAlias type="cn.java.entity.User" alias="User"/>
</typeAliases>
-->
<!-- 数据库配置文件,会自动解析 -->
<properties resource="jdbc.properties"></properties>
<!-- 设置环境 -->
<environments default="mysql">
<!-- 配置mysql -->
<environment id="mysql">
<!-- 配置事务,由JDBC实现 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<!-- 这里的${}不是EL表达式,而是OGNL表达式 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<!-- 使用p6spy打印日志(导入p6spy的jar包和spy.properties文件) -->
<environment id="p6spy">
<!-- 配置事务 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<!-- 这里的${}不是EL表达式,而是OGNL表达式 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- 关联局部配置文件(映射) -->
<mappers>
<mapper resource="cn/java/entity/User.xml"/>
<!-- 注解方式用class,并且写类全名 -->
<!-- <mapper class="cn.java.dao.UserDao"/> -->
</mappers>
</configuration>
导入log4j.properties:
书写 Mybatis的局部配置文件(存放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">
<!--
namespace:命名空间,其值为某一dao层类的具体路径,用于调用的映射
调用方式:namespace.id
-->
<mapper namespace="cn.java.entity.User">
<!--
sql语句必须保存在Mybatis的局部配置文件中
-->
<!--
select标签存放查询语句
id:整个配置文件中id值必须唯一,表示调用sql的名字,相当于方法名
resultType:指定当前sql语句返回的数据类型(存放其中一条数据的类型,而不是sql语句最终类型)
1 SQL语句中尽量不要出现 < > ,用< 和> 代替
2
返回值类型:
resultType:能够自动注入,前提是 数据库查询的列必须和成员变量一致,如果不一致,可以使用别名的方式或者手动映射
resultMap:数据需要手动注入,一般为查询列和成员变量不一致时使用,尤其是多表查询
参数类型:
parameterType:入参类型,写类全名,如果有别名可以使用别名代替
parameterMap:和resultMap想类似,基本上不用
-->
<select id="getAllUsers" resultType="cn.java.entity.User">
select * from users
</select>
<insert id="add" parameterType="cn.java.entity.User">
<!-- 这里的 #{username} 就等于是用PreparedStatement的 ? 方式,传递的参数会自动映射到username的属性上 -->
insert into users(username,password) values(#{username},#{password})
</insert>
<delete id="delete" parameterType="java.lang.Integer">
<!-- 如果有多个入参,也可以用#{arg0}代替#{id} -->
<!-- 如果是单个个入参,也可以用#{0}代替#{id} -->
delete from users where id=#{arg0}
</delete>
</mapper>
public interface UserDao {
/*
* 参数可以同过#{arg0},#{arg1}...的传入
*/
@Insert("insert into users(username,password) values(#{username},#{password})")
int add(String username, String password);
@Insert("insert into users(username,password) values(#{username},#{password})")
int addUser1(User user);
/*
* 无论是实体类,还是map集合传参,均可以通过key/属性名来获取值
*/
@Insert("insert into users(username,password) values(#{username},#{password})")
int addUser2(Map<String, String> userMap);
}
测试: 注: 增、删、改必须通过commit提交事务才生效
public class TestUser {
// @Before:在Test之前执行
// @After:在Test之后执行
// 查询操作
@Test
public void getAllUsers() throws IOException {
// SqlSession---->SqlSessionFactory---->SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sfb = new SqlSessionFactoryBuilder();
// 将mybatis.xml文件转化成流
InputStream ips = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory ssf = sfb.build(ips);
SqlSession session = ssf.openSession();
// 2、调用局部配置文件中的sql语句
// selectList:查询多条记录 selectOne:查询单条记录
List<User> selectList = session.selectList("cn.java.entity.User.getAllUsers");
for (User user : selectList) {
System.out.println(user);
}
}
@Test
public void add() throws IOException {
// SqlSession---->SqlSessionFactory---->SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sfb = new SqlSessionFactoryBuilder();
// 将mybatis.xml文件转化成流
InputStream ips = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory ssf = sfb.build(ips);
SqlSession session = ssf.openSession();
User user = new User("admin", "root");
Map<String, String> userMap = new HashMap<String, String>();
userMap.put("username", "admin");
userMap.put("password", "root");
// 局部配置文件xml方式
// int num = session.insert("cn.java.entity.User.add", user);
// 注解方式
// int num = session.insert("cn.java.dao.UserDao.addUser1",user);
// int num = session.insert("cn.java.dao.UserDao.addUser2", userMap);
session.getMapper(UserDao.class).addUser1(user);// 接口代理方式也是getMapper,接口代理需要局部配置文件和对应的接口,接口中不需要添加注解,主配置文件中映射局部配置文件
// 提交事务
session.commit();
// 关闭资源
session.close();
}
@Test
public void delete() throws IOException {
// SqlSession---->SqlSessionFactory---->SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sfb = new SqlSessionFactoryBuilder();
// 将mybatis.xml文件转化成流
InputStream ips = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory ssf = sfb.build(ips);
SqlSession session = ssf.openSession();
int num = session.delete("cn.java.entity.User.delete", 147);
session.commit();
session.close();
}
}
3.# 和 $ 的区别:
#获取参数值时,会解析成?,默认会把参数当做字符串处理,在参数之间会添加单引号
$ 不会解析成?,不会加单引号
4.动态SQL: if、choose(when、otherwise)、where等标签
public class StudentDaoImpl implements StudentDao {
@Override
public List<Student> list(Map<String, Object> map)
SqlSession session = MyBatisUtil.getSession();// MyBatisUtil 将获取SqlSession封装成一个工具类
List<Student> students = session.selectList("cn.java.dao.impl.StudentDaoImpl.list", map);
return students;
}
}
<?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="cn.java.dao.impl.StudentDaoImpl">
<!-- 需要传递多个参数的时候 就需要设置为map -->
<select id="list" parameterType="map" resultType="cn.java.entity.Student">
select * from students
<where>
<!-- 如果if中的test判断为false,则sql语句中不会有where -->
<if test="stuName!=null">
stuName like #{stuName}
</if>
</where>
<!--
# 会自动添加''
$ 不会添加''
-->
<if test="sort!=null">
order by ${sort}
<choose>
<when test="order!=null">
${order}
</when>
<!-- 默认 -->
<otherwise>asc</otherwise>
</choose>
</if>
</select>
</mapper>
5.resultMap:
resultType: 将查询结果集中的各个列--映射到java对象中与列名一致的属性中(只有列名与属性名相同,才能映射成功)
resultMap: 将查询结果集中的列--映射到java对象的各个属性上,这种映射是根据用户在resultMap的子标签中的配置来决定的
多对一查询:
public class Address {
private int id;
private String addr;
private String phone;
private String postcode;
private User user;// 将User对象封装到Address对象中,即多条address对应一个user
// (...省略构造方法,get/set方法)
}
AddressDaoImpl.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="cn.java.dao.impl.AddressDaoImpl">
<select id="load" parameterType="int" resultMap="addressMap" >
select * from t_address where id = #{id}
<!-- 表中列为:id,addr,phone,postcode,user_id -->
</select>
<!-- 在resultMap中设置关联关系 -->
<!--
type:是指对象所在的类
autoMapping:是否自动映射,如果为false,需要手动设置(result标签)每个属性
-->
<resultMap type="cn.java.entity.Address" id="addressMap" autoMapping="true">
<!--
property: 代表的是实体类中的属性名
javaType: 代表的是实体类中属性名的类型
column: 数据库中查询语句的字段名(与实体类属性向对应的,可以与实体类属性名不同)
jdbcType: 数据库中查询字段名的类型
<result property="" javaType="" column="">
-->
<!--
association : 联系,关联
property : Address中哪个变量保存关联对象(user)的引用
column : 数据库中的关联列,在数据库中以哪列来保存关联表的引用
javaType : 关联类的类型是什么(需要全名,使用设置的别名也可以)
select: 查询关联对象调用的方法,不用单独去写,可以直接调用User.xml中的查询方法即可
-->
<association property="user" column="user_id" javaType="cn.java.entity.User" select="cn.java.dao.impl.User.load"" />
<!--
User.load: select * from t_user where id=#{id}
-->
</resultMap>
</mapper>
第二种写法: 通过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="cn.java.dao.impl.AddressDaoImpl">
<select id="load" parameterType="int" resultMap="addrMap">
select * from t_address ta inner join t_user tu on ta.user_id=tu.id where ta.id=#{id}
</select>
<resultMap type="cn.java.entity.Address" id="addrMap" autoMapping="true">
<!--
id:其中为了多表查询得到多个结果时,确保数据的唯一性
如果不加id映射,则查询多条数据时,就会只有一条数据
如果当前查询只有一条结果数据,加不加都可以
-->
<id property="id" column="id"/>
<association property="user" column="user_id" javaType="cn.java.entity.User" resultMap="userMap"></association>
</resultMap>
<resultMap type="cn.java.entity.User" id="userMap" autoMapping="true">
<!--
因为结果集中的id是address的id,而不是user的id
当结果集中的列名与实体类中的变量不一致,需要手动映射,否则user的id就变成address的id值
-->
<result property="id" column="user_id"/>
</resultMap>
</mapper>
一对多查询:
public class User {
private int id;
private String username;
private String password;
private String nickname;
private int type;
private List<Address> addresses;// 把address封装到集合中
// (...省略get/set方法,构造方法)
}
UserDaoImpl.xml: 注: 向集合中映射时,collection中必须用ofType来设置集合中的元素类型
<?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="cn.java.dao.impl.UserDaoImpl">
<select id="load" parameterType="int" resultMap="userMap">
select *,ta.id as address_id from t_user tu inner join t_address ta on tu.id=ta.user_id where tu.id=#{id}
</select>
<resultMap type="cn.java.entity.User" id="userMap" autoMapping="true">
<id property="id" column="id"/>
<!-- 手动映射 -->
<!-- <result property="username" javaType="String" column="username"/> -->
<!--
collection:集合
property:User类中哪个变量存放这address的引用
ofType:集合中的元素类型
column:哪个列对应(用这个字段名来表示映射关系)(大多数是用于连接两个表的字段名)
-->
<collection property="addresses" ofType="cn.java.entity.Address" column="user_id" autoMapping="true">
<!-- 如果这里不用id,而用result进行映射,则不能区分唯一性,导致集合中只有一条元素 -->
<!-- 如果上面加了id映射,那么这里可以把id标签改写成result标签 -->
<id property="id" column="address_id"/>
<!-- <result property="addr" column="addr" jdbcType="VARCHAR"/> -->
</collection>
</resultMap>
</mapper>
6.调用存储过程:
@Test
public void proTest(){
Map<String,Object> paramMap = new HashMap<String,Object>();
paramMap.put("num1",10);
paramMap.put("num2",20);
session.selectOne("cn.java.dao.impl.GoodDaoImpl.proTest",paramMap);
System.out.println(paramMap);
}
GoodDaoImpl.xml:
<!--调用存储过程-->
<select id="proTest" parameterType="Map" statementType="CALLABLE">
call pro_sum(
#{num1,mode=IN,jdbcType=INTEGER},
#{num2,mode=IN,jdbcType=INTEGER},
#{sum,mode=OUT,jdbcType=INTEGER}
)
</select>