目录
JDBC
HiBernate
MyBatis
具体使用操作:
CRUD:
查询 Retrieve:
插入 Create:
更新 Update:
删除 Delete:
入参:
SQL 注入的问题:
结果集:
级联:
Association 一对一
Collection 一对多
缓存:
动态SQL
动态代理
JDK 动态代理
CGLIB字动态代理
JDBC
使用 JDBC 的 五个步骤:
1> 注册驱动和数据库信息。
2> 获得Connection,并使用它打开Statement对象。
3> 通过Statement对象执行SQL语句,并获得结果对象ResultSet。
4> 通过代码将ResultSet对象转化为POJO对象。
5> 关闭数据库资源。
缺点:
1> 代码量很大,繁琐。
2> 需要我们对异常进行正确捕获并关闭链接。
具体使用操作:
① 添加依赖
? ? ? ?<!-- JDBC -->
? ? ? ?<dependency>
? ? ? ? ? ?<groupId>mysql</groupId>
? ? ? ? ? ?<artifactId>mysql-connector-java</artifactId>
? ? ? ? ? ?<version>5.1.48</version>
? ? ? ?</dependency>
② 代码实现(UserExecutor.java)
public class UserExecutor {
? ?public static void main(String[] args) throws Throwable {
? ? ? ?// 1> 注册驱动和数据库信息。
? ? ? ?//Class.forName("com.mysql.cj.jdbc.Driver");
? ? ? ?Class.forName("com.mysql.jdbc.Driver");
?
? ? ? ?// 2> 获得Connection,并使用它打开Statement对象。
? ? ? ?Connection connection = null;
? ? ? ?PreparedStatement ps = null;
? ? ? ?ResultSet rs = null;
? ? ? ?try {
? ? ? ? ? ?connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/muse?useSSL=false","root", "123465");
?
? ? ? ? ? ?// 3> 通过Statement对象执行SQL语句,并获得结果对象ResultSet。
? ? ? ? ? ?// ps = connection.prepareStatement("select name, age from tb_user");
? ? ? ? ? ?// ps = ps.executeQuery();
?
? ? ? ? ? ?ps = connection.prepareStatement("select name, age from tb_user where id = ?");
? ? ? ? ? ?// 设置参数:第一个参数,设置为 1
? ? ? ? ? ?ps.setInt(1, 1);
? ? ? ? ? ?rs = ps.executeQuery();
?
? ? ? ? ? ?// 4> 通过代码将ResultSet对象转化为POJO对象。
? ? ? ? ? ?while (rs.next()) {
? ? ? ? ? ? ? ?System.out.println(
? ? ? ? ? ? ? ? ? ? ? ?String.format("姓名:%s, 年龄:%d", rs.getString("name"),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?rs.getInt("age")));
? ? ? ? ? }
? ? ? } catch (Throwable e) {
? ? ? ? ? ?System.out.println("error" + e.getMessage());
? ? ? } finally {
? ? ? ? ? ?close(connection, ps, rs);
? ? ? }
? }
?
? ?// 5> 关闭数据库资源。
? ?private static void close(Connection connection, PreparedStatement ps, ResultSet rs) {
? ? ? ?try {
? ? ? ? ? ?// 判断非空,且没有关闭才执行关闭
? ? ? ? ? ?if (rs != null && !rs.isClosed()) {
? ? ? ? ? ? ? ?rs.close();
? ? ? ? ? }
? ? ? } catch (Throwable e) {
? ? ? ? ? ?System.out.println("rs.close error! " + e.getMessage());
? ? ? }
?
? ? ? ?try {
? ? ? ? ? ?if (ps != null && !ps.isClosed()) {
? ? ? ? ? ? ? ?ps.close();
? ? ? ? ? }
? ? ? } catch (Throwable e) {
? ? ? ? ? ?System.out.println("ps.close error! " + e.getMessage());
? ? ? }
?
? ? ? ?try {
? ? ? ? ? ?if (connection != null && !connection.isClosed()) {
? ? ? ? ? ? ? ?connection.close();
? ? ? ? ? }
? ? ? } catch (Throwable e) {
? ? ? ? ? ?System.out.println("connection.close error! " + e.getMessage());
? ? ? }
? }
}
HiBernate
优点:
1> 将映射规则分离到XML/注解中,减少了代码的耦合度。
2> 无需管理数据库连接,只需配置响应的XML。
3> 一个会话,只需要操作Session对象即可。
4> 关闭资源,只关闭Session即可。
缺点:
1> 全表映射不便利,更新时需要发送所有字段。
2> 无法根据不同的条件组装不同的SQL。
3> 对于多表关联和复杂SQL查询支持较差,需要自己写SQL,返回后,需要自己将数据组装为POJO。
4> HQL性能较差,无法优化SQL。
5> 不能有效支持存储过程。
具体使用操作:
① 添加依赖
? ? ? ?<!-- Hibernate -->
? ? ? ?<dependency>
? ? ? ? ? ?<groupId>org.hibernate</groupId>
? ? ? ? ? ?<artifactId>hibernate-agroal</artifactId>
? ? ? ? ? ?<version>5.4.18.Final</version>
? ? ? ? ? ?<type>pom</type>
? ? ? ?</dependency>
② hibernate 配置文件(hibernate.conf.xml)
配置 数据库的相关属性 和 指定映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
? ? ? ?"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
? ? ? ?"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
? ?<!-- 配置数据库连接 connection -->
? ?<session-factory>
? ? ? ?<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
? ? ? ?<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/muse?useSSL=false&zeroDateTimeBehavior=CONVERT_TO_NULL</property>
? ? ? ?<property name="hibernate.connection.username">root</property>
? ? ? ?<property name="hibernate.connection.password">123465</property>
? ? ? ?<!-- 数据库方言 MySQL -->
? ? ? ?<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
? ? ? ?<!-- 格式化输出生成的SQL语句 -->
? ? ? ?<property name="hibernate.show_sql">true</property>
? ? ? ?<property name="hibernate.format_sql">true</property>
?
? ? ? ?<!-- 加载映射文件 -->
? ? ? ?<mapping resource="User.hbm.xml" />
? ?</session-factory>
</hibernate-configuration>
③ 映射文件(User.hbm.xml)
配置 实体类 和 表字段 的映射关系
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
? ? ? ?"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
? ? ? ?"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
? ?<class name="vo.User" table="tb_user">
? ? ? ?<!-- name 为实体类属性; column 为表字段 -->
? ? ? ?<id name="id" column="id">
? ? ? ? ? ?<!-- 主键策略,用于插入的时候进行主键维护 -->
? ? ? ? ? ?<generator class="native"/>
? ? ? ?</id>
? ? ? ?<property name="name" column="name"/>
? ? ? ?<property name="age" column="age"/>
? ?</class>
</hibernate-mapping>
④ 代码实现(UserExecutor.java)
public class UserExecutor {
? ?public static void main(String[] args) {
? ? ? ?// 读取配置文件
? ? ? ?Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
? ? ? ?// 通过配置打开一个 session
? ? ? ?SessionFactory sessionFactory = configuration.buildSessionFactory();
?
? ? ? ?Session session = null;
? ? ? ?try {
? ? ? ? ? ?// 一个会话,只需要操作Session对象即可
? ? ? ? ? ?session = sessionFactory.openSession();
? ? ? ? ? ?// 通过 get 方法获取 id 为 2 的数据
? ? ? ? ? ?User user = session.get(User.class, 2L);
? ? ? ? ? ?System.out.println("user = " + user);
? ? ? } catch (Throwable e) {
? ? ? ? ? ?System.out.println("error!" + e.getMessage());
? ? ? } finally {
? ? ? ? ? ?if (session != null) {
? ? ? ? ? ? ? ?session.close();
? ? ? ? ? ? ? ?sessionFactory.close();
? ? ? ? ? }
? ? ? }
? }
}
缺点:按照 hibernate 的方式拼装 sql 语句,不灵活,无法优化(sql 日志如下图)
MyBatis
1> 可以配置动态SQL。
2> 可以对SQL进行优化,并通过配置来决定SQL的映射规则。
3> 支持存储过程。
4> 具有自动映射功能,在注意命名规则的基础上,无需在写映射规则。
5> MyBatis提供接口编程的映射器,只需要一个接口和映射文件便可以运行【动态代理】。
6> 与代码耦合度低。
具体使用操作:
① 添加依赖
? ? ? ?<!-- MyBatis -->
? ? ? ?<dependency>
? ? ? ? ? ?<groupId>org.mybatis</groupId>
? ? ? ? ? ?<artifactId>mybatis</artifactId>
? ? ? ? ? ?<version>3.4.5</version>
? ? ? ?</dependency>
② 数据库环境配置文件(resources/sqlmap/mybatis/mysql/jdbc.properties)
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/muse?useUnicode=true&characterEncoding=utf-8&useSSL=false
username=root
password=123465
③ MyBatis 配置文件(resources/mybatis-config.xml)
<?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>
?
? ?<!-- 加载数据库配置文件 -->
? ?<properties resource="sqlmap/mybatis/mysql/jdbc.properties" />
?
? ?<settings>
? ? ? ?<setting name="logImpl" value="STDOUT_LOGGING"/> <!-- 打印查询语句 -->
? ?</settings>
?
? ?<!-- 别名 -->
? ?<typeAliases>
? ? ? ?<package name="vo"/>
? ?</typeAliases>
?
? ?<!-- 配置数据库环境 -->
? ?<environments default="dev">
? ? ? ?<environment id="dev">
? ? ? ? ? ?<transactionManager type="JDBC"/>
? ? ? ? ? ?<dataSource type="POOLED">
? ? ? ? ? ? ? ?<property name="driver" value="${driver}"/>
? ? ? ? ? ? ? ?<property name="url" value="${url}"/>
? ? ? ? ? ? ? ?<property name="username" value="${username}"/>
? ? ? ? ? ? ? ?<property name="password" value="${password}"/>
? ? ? ? ? ?</dataSource>
? ? ? ?</environment>
? ?</environments>
?
? ?<!-- 数据库厂商标识 -->
? ?<databaseIdProvider type="DB_VENDOR"/>
?
? ?<!-- mappers 映射器 -->
? ?<mappers>
? ? ? ?<mapper resource="sqlmap/mybatis/mysql/UserMapper.xml"/>
? ?</mappers>
</configuration>
MyBatis提供接口编程的映射器,只需要一个接口和映射文件便可以运行
④ 映射文件(resources/sqlmap/mybatis/mysql/UserMapper.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace 对应着接口名称(下面⑤的接口名称) -->
<mapper namespace="mybatis.mapper.UserMapper">
?
? ?<sql id="allColumn">
? ? ? id, name, age
? ?</sql>
?
? ?<select id="getUserById" parameterType="long" resultType="user">
? ? ? select
? ? ? ? ? ?<include refid="allColumn"/>
? ? ? from
? ? ? ? ? tb_user
? ? ? where
? ? ? ? ? id = #{id}
? ?</select>
?
</mapper>
⑤ 接口(src/main/java/mybatis/mapper/UserMapper.java)
public interface UserMapper {
?
? ?User getUserById(@Param("id") Long id);
?
}
⑥ 实例一个 sqlSession(src/main/java/mybatis/common/SqlSessionFactoryUtil.java)
public class SqlSessionFactoryUtil {
?
? ?private static SqlSessionFactory sqlSessionFactory;
?
? ?public static SqlSession openSqlSession() {
? ? ? ?// 单例实例一个 session
? ? ? ?// 第一次判断,提供效率
? ? ? ?if (sqlSessionFactory == null) {
? ? ? ? ? ?init();
? ? ? }
? ? ? ?return sqlSessionFactory.openSession(true);
? }
?
? ?private static SqlSessionFactory init() {
? ? ? ?InputStream inputStream = null;
? ? ? ?try {
? ? ? ? ? ?// 读取配置资源
? ? ? ? ? ?inputStream = Resources.getResourceAsStream("mybatis-config.xml");
? ? ? } catch (IOException e) {
? ? ? ? ? ?e.printStackTrace();
? ? ? }
?
? ? ? ?synchronized(SqlSessionFactoryUtil.class) {
? ? ? ? ? ?// 二次判断,判断是否已经有实例
? ? ? ? ? ?if (sqlSessionFactory == null) {
? ? ? ? ? ? ? ?sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
? ? ? ? ? }
? ? ? ? ? ?return sqlSessionFactory;
? ? ? }
? }
}
⑦ 通过 session 调用接口操作数据库(src/main/java/mybatis/UserExecuter.java)
public class UserExecuter {
? ?public static void main(String[] args) {
? ? ? ?SqlSession sqlSession = null;
? ? ? ?try {
? ? ? ? ? ?// 获取 session
? ? ? ? ? ?sqlSession = SqlSessionFactoryUtil.openSqlSession();
? ? ? ? ? ?// 获得接口
? ? ? ? ? ?UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
? ? ? ? ? ?// 使用接口操作数据库
? ? ? ? ? ?User user = userMapper.getUserById(2L);
? ? ? ? ? ?System.out.println("user = " + user);
? ? ? } catch (Throwable e) {
? ? ? ? ? ?System.out.println("error!" + e.getMessage());
? ? ? } finally {
? ? ? ? ? ?// 关闭 session
? ? ? ? ? ?if (sqlSession != null) {
? ? ? ? ? ? ? ?sqlSession.close();
? ? ? ? ? }
? ? ? }
? }
}
相比于 hibernate 的 sql 拼装,mybatis 的 sql 没有进行固定格式的格式,操作优化简单。
CRUD:
查询 Retrieve:
(1)基础查询:
① mybatis-config.xml 添加映射器
? ?<!-- mappers 映射器 -->
? ?<mappers>
? ? ? ?<mapper resource="sqlmap/mybatis/mysql/UserMapper.xml"/>
? ? ? ?<mapper resource="sqlmap/mybatis/mysql/UserContactMapper.xml"/>
? ?</mappers>
② 新建接口 MessageMapper.java
public interface MessageMapper {
?
? ?Message getMessageById(@Param("id") Long id);
? ?
}
③ 新建映射器 MessageMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mybatis.mapper.MessageMapper">
?
? ?<sql id="allColumns">
? ? ? id, msg_id, status, content, deleted, create_time, update_time
? ?</sql>
?
?
? ?<select id="getMessageById" parameterType="long" resultType="message">
? ? ? select
? ? ? ? ? ?<include refid="allColumns"/>
? ? ? from
? ? ? ? ? tb_message
? ? ? where
? ? ? ? ? id = #{id}
? ?</select>
?
</mapper>
④ 一个映射器,一个接口就能运行,测试类 MessageExecur.java
public class MessageExecuter {
? ?public static void main(String[] args) {
?
? ? ? ?SqlSession sqlSession = null;
? ? ? ?try {
? ? ? ? ? ?sqlSession = SqlSessionFactoryUtil.openSqlSession();
? ? ? ? ? ?MessageMapper messageMapper = sqlSession.getMapper(MessageMapper.class);
?
? ? ? ? ? ?/** 基础类型查询 **/
? ? ? ? ? ?Message message = messageMapper.getMessageById(1L);
? ? ? ? ? ?System.out.println("message = " + message);
?
? ? ? } catch (Throwable e) {
? ? ? ? ? ?System.out.println("error!" + e.getMessage());
? ? ? } finally {
? ? ? ? ? ?if (sqlSession != null) {
? ? ? ? ? ? ? ?sqlSession.close();
? ? ? ? ? }
? ? ? }
? }
}
结果如下:
发现输出的实体结果:不为空的字段输出为空。
原因是:没有驼峰字段的映射?
解决方案:① 配置中开启自动驼峰映射
<!-- 自动映射的三种行为:不映射;部分映射;全部映射 NONE PARTIAL FULL -->
<setting name="autoMappingBehavior" value="PARTIAL"/>
<!-- 配置驼峰转下划线 数据库中的下划线,转换Java Bean中的驼峰 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
② 设置结果集映射 resultMap
<resultMap id="messageVoReusltMap" type="vo.MessageVO">
? ? ? ?<id column="id" property="idVo"/>
? ? ? ?<result column="msg_id" property="msgIdVo"/>
? ? ? ?<result column="status" property="statusVo"/>
? ? ? ?<result column="content" property="contentVo"/>
? ? ? ?<result column="deleted" property="deletedVo"/>
? ? ? ?<result column="create_time" property="createTimeVo"/>
? ? ? ?<result column="update_time" property="updateTimeVo"/>
? ?</resultMap>
<select id="getMessageVOById" parameterType="long" resultMap="messageVoReusltMap">
? ? ? select
? ? ? ? ? ?<include refid="allColumns"/>
? ? ? from
? ? ? ? ? tb_message
? ? ? where
? ? ? ? ? id = #{id}
? ?</select>
③ 结果:
(2)以 map 作为入参进行多个参数的查询
① 接口 MessageMapper.java
public interface MessageMapper {
?
? ?Message getMessageByMap(Map<String, Object> params);
? ?
}
② 映射器 MessageMapper.xml
<select id="getMessageByMap" parameterType="map" resultType="message">
? ? ? ?select
? ? ? ? ? ?<include refid="allColumns"/>
? ? ? ?from
? ? ? ? ? ?tb_message
? ? ? ?where
? ? ? ? ? ?id = #{id} and msg_id = #{msgId}
? ?</select>
③ 测试类 MessageExecur.java
Map<String, Object> params = new HashMap<String, Object>();
params.put("id", 1L);
params.put("msgId", "msg_1");
Message message = messageMapper.getMessageByMap(params);
System.out.println("message = " + message);
④ 结果:
?
(3)以注解的方式,指定查询入参
① 接口 MessageMapper.java
public interface MessageMapper {
?
? ?Message getMessageByIdAndMsgId(@Param("id") Long id, @Param("msgId") String msgId);
? ?
}
② 映射器 MessageMapper.xml
<select id="getMessageByIdAndMsgId" resultType="message">
? ? ? ?select
? ? ? ? ? ?<include refid="allColumns"/>
? ? ? ?from
? ? ? ? ? ?tb_message
? ? ? ?where
? ? ? ? ? ?id = #{id} and msg_id = #{msgId}
? ?</select>
③ 测试类 MessageExecur.java
Message message = messageMapper.getMessageByIdAndMsgId(1L, "msg_1");
System.out.println("message = " + message);
(4)以实体的是方式,指定查询入参
① 接口 MessageMapper.java
public interface MessageMapper {
?
? ?Message getMessageByMessage(Message message);
? ?
}
② 映射器 MessageMapper.xml
<select id="getMessageByMessage" parameterType="vo.Message" resultType="message">
? ? ? ?select
? ? ? ? ? ?<include refid="allColumns"/>
? ? ? ?from
? ? ? ? ? ?tb_message
? ? ? ?where
? ? ? ? ? ?id = #{id} and msg_id = #{msgId}
? ?</select>
③ 测试类 MessageExecur.java
Message params = new Message();
params.setId(1L);
params.setMsgId("msg_1");
Message message = messageMapper.getMessageByMessage(params);
System.out.println("message = " + message);
插入 Create:
① 接口 MessageMapper.java
public interface MessageMapper {
?
? ?int insert(Message message);
? ?
}
② 映射器 MessageMapper.xml
<sql id="insertAllColumns">
? ? ? ?msg_id, status, content, deleted, create_time
? ?</sql>
?
<insert id="insert" parameterType="message" keyProperty="id" >
? ? ? ?insert into tb_message(<include refid="insertAllColumns"/>) values(#{msgId}, #{status}, #{content},#{deleted}, #{createTime})
? ?</insert>
③ 测试类 MessageExecur.java
Message message = new Message();
message.setMsgId("msg_5");
message.setDeleted(0);
message.setStatus(1);
message.setContent("dddd");
message.setCreateTime(new Date());
messageMapper.insert(message);
?
sqlSession.commit();
System.out.println("message = " + message);
④ 结果:
从结果可以看到,插入已经成功了,但直接输出的话,插入生成的数据并没有回显。
想要回显的话:① 再查一遍?
② 配置 useGeneratedKeys="true"
<insert id="insert" parameterType="message" keyProperty="id" useGeneratedKeys="true">
? ? ? insert into tb_message(<include refid="insertAllColumns"/>) values(#{msgId}, #{status}, #{content},#{deleted}, #{createTime})
? ?</insert>
⑤ 配置自动提交
配置了自动提交之后,上面测试类中的提交就可以不用了:// sqlSession.commit();
在 SqlSessionFactoryUtil.java 中给 openSession() 加上参数 true;
public static SqlSession openSqlSession() {
? ? ? ?if (sqlSessionFactory == null) {
? ? ? ? ? ?init();
? ? ? }
? ? ? ?return sqlSessionFactory.openSession(true);
? }
更新 Update:
① 接口 MessageMapper.java
public interface MessageMapper {
?
? ?int updateContentById(@Param("id") Long id, @Param("content") String content);
? ?
}
② 映射器 MessageMapper.xml
<update id="updateContentById">
? ? ? ?update tb_message set content=#{content} where id = #{id}
? ?</update>
③ 测试类 MessageExecur.java
messageMapper.updateContentById(1L, "newContent1");
删除 Delete:
① 接口 MessageMapper.java
public interface MessageMapper {
?
? ?int deleteById(Long id);
? ?
}
② 映射器 MessageMapper.xml
<delete id="deleteById" parameterType="long">
? ? ? ?delete from tb_message where id = #{id}
? ?</delete>
③ 测试类 MessageExecur.java
messageMapper.deleteById(12L);
入参:
$与#的区别?
? $ 采用 值传递 的方式构建 SQL 语句
? # 采用 预编译 的方式构建 SQL 语句
举个例子:
① 接口 MessageMapper.java
public interface MessageMapper {
?
? ?Message getMessageByMsgId(@Param("msgId") String msgId);
? ?
}
② 映射器 MessageMapper.xml
<select id="getMessageByMsgId" resultType="message">
? ? ? ?select
? ? ? ? ? ?<include refid="allColumns"/>
? ? ? ?from
? ? ? ? ? ?tb_message
? ? ? ?where
? ? ? ? ? ?msg_id = #{msgId}
? ?</select>
③ 测试类 MessageExecur.java
Message message = messageMapper.getMessageByMsgId("msg_1");
System.out.println(message);
④ 结果:
#
<select id="getMessageByMsgId" resultType="message">
? select
? ? ? ?<include refid="allColumns"/>
? from
? ? ? tb_message
? where
? ? ? msg_id = #{msgId}
</select>
?
$
<select id="getMessageByMsgId" resultType="message">
? select
? ? ? ?<include refid="allColumns"/>
? from
? ? ? tb_message
? where
? ? ? msg_id = ${msgId}
</select>
?
SQL 注入的问题:
① 接口 MessageMapper.java
public interface MessageMapper {
?
? ?List<Message> getMessageByStringId(@Param("id") String id);
? ?
}
② 映射器 MessageMapper.xml
<select id="getMessageByStringId" parameterType="string" resultType="message">
? ? ? ?select
? ? ? ? ? ?<include refid="allColumns"/>
? ? ? ?from
? ? ? ? ? ?tb_message
? ? ? ?where
? ? ? ? ? ?id = #{id}
? ?</select>
③ 测试类 MessageExecur.java
List<Message> message = messageMapper
? ? ? ? ? ? ? ? ? .getMessageByStringId("1 or (select count(1) from tb_message) > 0");
? ? ? ? ? ?System.out.println("message = " + message);
④ 结果:
#
$
?
结果集:
存储结果集方式有哪些?
? map 方式存储结果集
? POJO 方式存储结果集
map:
① 接口 MessageMapper.java
public interface MessageMapper {
?
? ?Map getMessageMapById(@Param("id") Long id);
? ?
}
② 映射器 MessageMapper.xml
<select id="getMessageMapById" parameterType="long" resultType="map">
? ? ? ?select
? ? ? ? ? ?<include refid="allColumns"/>
? ? ? ?from
? ? ? ? ? ?tb_message
? ? ? ?where
? ? ? ? ? ?id = #{id}
? ?</select>
③ 测试类 MessageExecur.java
Map map = messageMapper.getMessageMapById(1L);
System.out.println("message = " + map);
pojo:
① 接口 MessageMapper.java
public interface MessageMapper {
?
? ?Message getMessageByMsgId(@Param("msgId") String msgId);
? ?
}
② 映射器 MessageMapper.xml
<select id="getMessageByMsgId" resultType="message">
? ? ? ?select
? ? ? ? ? ?<include refid="allColumns"/>
? ? ? ?from
? ? ? ? ? ?tb_message
? ? ? ?where
? ? ? ? ? ?msg_id = #{msgId}
? ?</select>
③ 测试类 MessageExecur.java
Message message = messageMapper.getMessageByMsgId("msg_1");
System.out.println(message);
级联:
Association 一对一
通过 id 使用 messageMapper 接口查询 message 信息,同时通过 一对一 级联查询 messageDetail 信息。
① 接口
MessageMapper.java
public interface MessageMapper {
?
? ?Map getMessageMapById(@Param("id") Long id);
? ?
}
MessageDetailMapper.java
public interface MessageDetailMapper {
?
? ?MessageDetail getMessageByMsgId(@Param("msgId") String msgId);
? ?
}
② 映射器
MessageDetailMapper.xml
<sql id="allColumns">
? ? ? id, msg_id, detail_content, create_time, update_time
? ?</sql>
?
? ?<select id="getMessageByMsgId" parameterType="string" resultType="vo.MessageDetail">
? ? ? select
? ? ? ? ? ?<include refid="allColumns"/>
? ? ? from
? ? ? ? ? tb_message_detail
? ? ? where
? ? ? ? ? msg_id = #{msgId}
? ?</select>
MessageMapper.xml
【 级联查询在 resultMap 中的 association 部分 】
<resultMap id="messageAndDetailReusltMap" type="vo.Message">
? ? ? ?<id column="id" property="id"/>
? ? ? ?<result column="msg_id" property="msgId"/>
? ? ? ?<result column="status" property="status"/>
? ? ? ?<result column="content" property="content"/>
? ? ? ?<result column="deleted" property="deleted"/>
? ? ? ?<result column="create_time" property="createTime"/>
? ? ? ?<result column="update_time" property="updateTime"/>
? ? ? ?<association property="messageDetail" column="msg_id"
? ? ? ? ? ? ? ? ? ? select="mybatis.mapper.MessageDetailMapper.getMessageByMsgId" />
? ?</resultMap>
?
<select id="getMessageAndMessageDetailById" parameterType="long" resultMap="messageAndDetailReusltMap">
? ? ? ?select
? ? ? ? ? ?<include refid="allColumns"/>
? ? ? ?from
? ? ? ? ? ?tb_message
? ? ? ?where
? ? ? ? ? ?id = #{id}
? ?</select>
③ 测试类 MessageExecur.java
Message message = messageMapper.getMessageAndMessageDetailById(1L);
System.out.println("message = " + message);
④ 结果:
?
Collection 一对多
通过 id 使用 UserMapper接口查询 user 信息,同时通过 一对多 级联查询 UserContact 信息。
① 接口
UserMapper.java
public interface UserMapper {
?
? ?User getUserAndUserContactById(@Param("id") Long id);
?
}
UserContactMapper.java
public interface UserContactMapper {
?
? ?List<UserContact> getUserContactByUserId(@Param("userId") Long userId);
}
② 映射器
UserContactMapper.xml
<sql id="allColumn">
? ? ? id, user_id, contact_type, contact_value, create_time, update_time
? ?</sql>
?
? ?<select id="getUserContactByUserId" parameterType="long" resultType="vo.UserContact">
? ? ? select
? ? ? ? ? ?<include refid="allColumn"/>
? ? ? from
? ? ? ? ? tb_user_contact
? ? ? where
? ? ? ? ? user_id=#{userId}
? ?</select>
UserMapper.xml
【 级联查询在 resultMap 中的 collection 部分 】
<resultMap id="userReuslt" type="vo.User">
? ? ? ?<id column="id" property="id"/>
? ? ? ?<result column="name" property="name"/>
? ? ? ?<result column="age" property="age"/>
? ? ? ?<collection property="userContacts" column="id"
? ? ? ? ? ? ? ? ? ?select="mybatis.mapper.UserContactMapper.getUserContactByUserId" />
? ?</resultMap>
?
<select id="getUserAndUserContactById" parameterType="long" resultMap="userReuslt">
? ? ? ?select
? ? ? ? ? ?<include refid="allColumn"/>
? ? ? ?from
? ? ? ? ? ?tb_user
? ? ? ?where
? ? ? ? ? ?id = #{id}
? ?</select>
③ 测试类 MessageExecur.java
User user = userMapper.getUserAndUserContactById(1L);
System.out.println("user = " + user);
④ 结果:
?
缓存:
一级缓存
一级缓存默认开启
?但一级缓存只针对同一个 session,新 session 会失效
?
二级缓存
开启:
① 添加 cache 标签
② 手动提交
?
结果:
?
动态SQL
if
所有 if 都满足才有结果
① 接口 UserMapper.java
public interface UserMapper {
?
? ?List<User> getUserByUser(User user);
?
}
② 映射器 UserMapper.xml
<resultMap id="userResultMap" type="vo.User">
? ? ? ?<id column="id" property="id"/>
? ? ? ?<result column="name" property="name"/>
? ? ? ?<result column="age" property="age"/>
? ?</resultMap>
?
?
<select id="getUserByUser" parameterType="vo.User" resultMap="userResultMap">
? ? ? select id, name, age
? ? ? from tb_user
? ? ? where 1=1
? ? ? ?<if test="id != null">
? ? ? ? ? and id = #{id}
? ? ? ?</if>
? ? ? ?<if test="name != null and name != ''">
? ? ? ? ? and name = #{name}
? ? ? ?</if>
? ? ? ?<if test="age != null">
? ? ? ? ? and age = #{age}
? ? ? ?</if>
? ?</select>
③ 测试 UserExecuter.java
User userParam = new User();
userParam.setName("muse");
userParam.setId(1L);
List<User> user = userMapper.getUserByUser(userParam);
System.out.println("user = " + user);
choose--when--otherwise
choose 标签是按顺序判断其内部 when 标签中的 test 条件出否成立,如果有一个成立,则 choose 结束。当choose 中所有 when 的条件都不满则时,则执行 otherwise 中的 sql。类似于 Java 的 switch 语句,choose 为 switch,when 为 case,otherwise 则为 default。
① 接口 UserMapper.java
public interface UserMapper {
?
? ?List<User> getUserByUser2(User user);
?
}
② 映射器 UserMapper.xml
<select id="getUserByUser2" parameterType="vo.User" resultMap="userResultMap">
? ? ? select id, name, age
? ? ? from tb_user
? ? ? where 1=1
? ? ? ?<choose>
? ? ? ? ? ?<when test="id != null">
? ? ? ? ? ? ? and id = #{id}
? ? ? ? ? ?</when>
? ? ? ? ? ?<when test="name != null and name != ''">
? ? ? ? ? ? ? and name = #{name}
? ? ? ? ? ?</when>
? ? ? ? ? ?<otherwise>
? ? ? ? ? ? ? and age is not null
? ? ? ? ? ?</otherwise>
? ? ? ?</choose>
? ?</select>
③ 测试 UserExecuter.java
User userParam = new User();
userParam.setName("muse");
userParam.setId(1L);
userParam.setAge(22);
List<User> user = userMapper.getUserByUser2(userParam);
System.out.println("user = " + user);
where
① 接口 UserMapper.java
public interface UserMapper {
?
? ?List<User> getUserByUser3(User user);
?
}
② 映射器 UserMapper.xml
<select id="getUserByUser3" parameterType="vo.User" resultMap="userResultMap">
? ? ? select
? ? ? id, name, age from tb_user
? ? ? ?<where>
? ? ? ? ? ?<if test="id != null">
? ? ? ? ? ? ? and id = #{id}
? ? ? ? ? ?</if>
? ? ? ?</where>
? ?</select>
③ 测试 UserExecuter.java
User userParam = new User();
userParam.setId(1L);
List<User> user = userMapper.getUserByUser3(userParam);
System.out.println("user = " + user);
trim
mybatis 的 trim 标签一般用于去除 sql 语句中多余的 关键字,逗号,或者给 sql 语句前拼接 “where“、“set“以及“values(“ 等前缀,或者添加“)“等后缀,可用于选择性插入、更新、删除或者条件查询等操作。
属性 | 描述 |
---|
prefix | 给sql语句拼接的前缀 | suffix | 给sql语句拼接的后缀 | prefixOverrides | 去除sql语句前面的关键字或者字符 | suffixOverrides | 去除sql语句后面的关键字或者字符 |
参考:mybatis trim标签的使用_wt_better的博客-CSDN博客_mybatis trim
① 接口 UserMapper.java
public interface UserMapper {
?
? ?List<User> getUserByUser4(User user);
?
}
② 映射器 UserMapper.xml
<select id="getUserByUser4" parameterType="vo.User" resultMap="userResultMap">
? ? ? select
? ? ? id, name, age from tb_user
? ? ? ?<trim prefix="where" prefixOverrides="and">
? ? ? ? ? and id = #{id}
? ? ? ?</trim>
? ?</select>
③ 测试 UserExecuter.java
User userParam = new User();
userParam.setId(1L);
List<User> user = userMapper.getUserByUser4(userParam);
System.out.println("user = " + user);
set
用于修改
① 接口 UserMapper.java
public interface UserMapper {
?
? ?int updateUserByUser(User user);
?
}
② 映射器 UserMapper.xml
<update id="updateUserByUser" parameterType="vo.User">
? ? ? update tb_user
? ? ? ?<set>
? ? ? ? ? ?<if test="name != null and name != ''">
? ? ? ? ? ? ? name = #{name},
? ? ? ? ? ?</if>
? ? ? ? ? ?<if test="age != null">
? ? ? ? ? ? ? age = #{age},
? ? ? ? ? ?</if>
? ? ? ?</set>
? ? ? where id = #{id}
? ?</update>
③ 测试 UserExecuter.java
User userParam = new User();
userParam.setId(1L);
userParam.setAge(222);
userMapper.updateUserByUser(userParam);
foreach
① 接口 UserMapper.java
public interface UserMapper {
?
? ?List<User> getUserByIds(@Param("idList") List<Long> idList);
?
}
② 映射器 UserMapper.xml
<select id="getUserByIds" resultMap="userResultMap">
? ? ? select
? ? ? id, name, age
? ? ? from tb_user
? ? ? where id in
? ? ? ?<foreach collection="idList" index="index" item="id" open="(" separator="," close=")">
? ? ? ? ? #{id}
? ? ? ?</foreach>
? ?</select>
③ 测试 UserExecuter.java
List<Long> ids = new ArrayList();
ids.add(1L);
ids.add(2L);
ids.add(3L);
ids.add(4L);
List<User> users = userMapper.getUserByIds(ids);
System.out.println("users = " + users);
concat & bind
都是用来模糊查询
① 接口 UserMapper.java
public interface UserMapper {
?
? ?List<User> getUserByName(@Param("name") String name);
?
}
② 映射器 UserMapper.xml
<select id="getUserByName" parameterType="string" resultMap="userResultMap">
? ? ? ?<bind name="namePattern" value="'%' + name + '%'"/>
? ? ? select
? ? ? id, name, age
? ? ? from
? ? ? tb_user
? ? ? where
? ? ? name like #{namePattern}
? ? ? ?<!-- name like concat('%', #{name}, '%') -->
? ?</select>
③ 测试 UserExecuter.java
List<User> users = userMapper.getUserByName("muse");
System.out.println("users = " + users);
动态代理
JDK 动态代理
JDK 动态代理是利用 反射机制 生成一个实现代理接口的匿名类,在调用具体方法前调用 InvokeHandler 进行代理处理。
① 接口
public interface MessageService {
? ?void sendMessage();
}
② 接口实现(被代理对象)
public class MessageServiceImpl implements MessageService {
? ?@Override
? ?public void sendMessage() {
? ? ? ?System.out.println("MessageServiceImpl.sendMessage");
? }
}
③ 代理类 【实现 InvocationHandler 接口】
public class JdkProxy<T> implements InvocationHandler {
?
? ?T target;
?
? ?public T getProxy(T target) {
? ? ? ?this.target = target;
? ? ? ?return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
? }
?
? ?@Override
? ?public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
? ? ? ?System.out.println("JDK动态代理拦截开始!");
? ? ? ?Object result = method.invoke(target, args);
? ? ? ?System.out.println("JDK动态代理拦截结束!");
? ? ? ?return result;
? }
}
④ 测试
public class Executer {
? ?public static void main(String[] args) {
? ? ? ?JdkProxy<MessageService> jdkProxy = new JdkProxy();
? ? ? ?MessageService messageService = jdkProxy.getProxy(new MessageServiceImpl());
? ? ? ?messageService.sendMessage();
? }
}
CGLIB字动态代理
cglib 动态代理是利用 asm 开源包,将代理对象类的 clas s文件加载进来,通过修改其 字节码 生成子类来进行处理。
① 被代理对象类
public class PlayService {
? ?public void play() {
? ? ? ?System.out.println("PlayService.play");
? }
}
② 代理类 【实现 MethodInterceptor 接口】
public class CglibProxy<T> implements MethodInterceptor {
?
? ?T target;
?
? ?public T getProxy(T target) {
? ? ? ?this.target = target;
? ? ? ?Enhancer enhancer = new Enhancer();
? ? ? ?enhancer.setSuperclass(target.getClass());
? ? ? ?enhancer.setCallback(this);
? ? ? ?return (T) enhancer.create();
? }
?
? ?public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
? ? ? ?System.out.println("CGLIB动态代理拦截开始!");
? ? ? ?Object result = methodProxy.invokeSuper(o, objects);
? ? ? ?System.out.println("CGLIB动态代理拦截结束!");
? ? ? ?return result;
? }
}
③ 测试
public class Executer {
? ?public static void main(String[] args) {
? ? ? ?CglibProxy<PlayService> cglibProxy = new CglibProxy();
? ? ? ?// 代理无接口服务类
? ? ? ?PlayService playService = cglibProxy.getProxy(new PlayService());
? ? ? ?playService.play();
?
// ? ? ? CglibProxy<MessageService> cglibProxy1 = new CglibProxy();
// ? ? ? // 代理有接口服务类【cglib动态代理:有无接口都可】
// ? ? ? MessageService messageService = cglibProxy1.getProxy(new MessageServiceImpl());
// ? ? ? messageService.sendMessage();
? }
}
|