1.实现接口TypeHandler
2.继承BaseTypeHandler
表结构
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userName` varchar(50) DEFAULT NULL,
`userAge` int(11) DEFAULT NULL,
`userAddress` varchar(200) DEFAULT NULL,
`reg_time` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
/**
*
* @ProjectName: springmvc-mybatis
* @Description: 自定义处理日期Hander,
* 将Date类型转换为时间戳字符串戳存入到数据库中
* 1.@MappedJdbcTypes定义的是JdbcType类型,这里的类型不可自己随意定义,
* 必须要是枚举类org.apache.ibatis.type.JdbcType所枚举的数据类型。
* 2.@MappedTypes定义的是JavaType的数据类型,描述了哪些Java类型可被拦截。
* 3.在我们启用了我们自定义的这个TypeHandler之后,数据的读写都会被这个类所过滤
*/
@MappedTypes({Date.class})
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyDateTypeHandler extends BaseTypeHandler<Date> {
/**
* 将时间戳字符串存入数据库
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, String.valueOf(parameter.getTime()));
}
/**
* 把时间戳类型的字符串取出转换为Date
*/
@Override
public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
long timeLong = Long.parseLong(rs.getString(columnName));
Date date = new Date();
date.setTime(timeLong);
return date;
}
/**
* 把时间戳类型的字符串取出转换为Date
*/
@Override
public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
long timeLong = Long.parseLong(rs.getString(columnIndex));
Date date = new Date();
date.setTime(timeLong);
return date;
}
/**
* 把时间戳类型的字符串取出转换为Date
*/
@Override
public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
long timeLong = Long.parseLong(cs.getString(columnName));
Date date = new Date();
date.setTime(timeLong);
return date;
}
}
<resultMap id="resultListUser" type="User">
<id column="id" property="id" />
<result column="userName" property="userName" />
<result column="userAge" property="userAge" />
<result column="userAddress" property="userAddress" />
<result column="reg_time" property="regTime" javaType="java.util.Date" jdbcType="VARCHAR" typeHandler="com.wdzl.mybatis.type.MyDateTypeHandler"/>
resultMap>
<select id="findUserById" parameterType="int" resultMap="resultListUser">
select * from user where id = #{id}
select>
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="uid">
insert into user(userName,userAge,userAddress,reg_time) values(#{userName},#{userAge}, #{userAddress},
#{regTime,javaType=Date,jdbcType=VARCHAR,typeHandler=com.wdzl.mybatis.type.MyDateTypeHandler})
insert>
@Repository
public interface UserMapper {
public User findUserById(int uid);
public void insertUser(User user);
}
/** * 添加 */
public class TestAddUser {
private static SqlSessionFactory sqlSessionFactory;
private static Reader reader;
static {
try {
reader = Resources.getResourceAsReader("configuration.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSession() {
return sqlSessionFactory;
}
public static void main(String[] args) {
insertUser();
}
public static void insertUser() {
User user = new User();
user.setUserName("spring");
user.setUserAge("101");
user.setUserAddress("hangzhou,xihu");
user.setRegTime(new Date());
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.addUser(user);
session.commit();
System.out.println("当前增加的用户 id为:" + user.getId());
} catch (Exception e) {
session.rollback();
}finally {
session.close();
}
}
}
package user;
import com.wdzl.mybatis.dao.UserMapper;
import com.wdzl.mybatis.model.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 java.io.IOException;
import java.io.Reader;
import java.util.List;
/**
*
* @ProjectName: springmvc-mybatis
* @Description: 查找
*/
public class TestGetUser {
private static SqlSessionFactory sqlSessionFactory;
private static Reader reader;
static {
try {
reader = Resources.getResourceAsReader("configuration.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSession() {
return sqlSessionFactory;
}
public static void findUserById(int id){
SqlSession session = sqlSessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
List<User> users = userMapper.selectUserById2(id);
for(User u : users) {
System.out.println(u.getRegTime());
}
}
public static void main(String[] args) {
findUserById(3);
}
}
局部配置:
<resultMap id="resultListUser" type="User">
<id column="id" property="id" />
<result column="userName" property="userName" />
<result column="userAge" property="userAge" />
<result column="userAddress" property="userAddress" />
<result column="reg_time" property="regTime" javaType="java.util.Date" jdbcType="VARCHAR" typeHandler="com.wdzl.mybatis.type.MyDateTypeHandler"/>
resultMap>
<select id="findUserById" parameterType="int" resultMap="resultListUser">
select * from user where id = #{id}
select>
全局配置:
<typeHandlers>
<typeHandler handler="com.wdzl.mybatis.type.MyDateTypeHandler" />
typeHandlers>
商品表(items):
CREATE TABLE `items` (
`iid` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL COMMENT '商品名称',
`price` FLOAT(10,1) NOT NULL COMMENT '商品定价',
`detail` TEXT COMMENT '商品描述',
`pic` VARCHAR(64) DEFAULT NULL COMMENT '商品图片',
`createtime` DATETIME NOT NULL COMMENT '生产日期',
PRIMARY KEY (`iid`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
商品表插入数据:
INSERT INTO `items` VALUES (1, '台式机', '3000.0', '该电脑质量非常好!!!!', null, '2015-02-03 13:22:53');
INSERT INTO `items` VALUES (2, '笔记本', '6000.0', '笔记本性能好,质量好!!!!!', null, '2015-02-09 13:22:57');
INSERT INTO `items` VALUES (3, '背包', '200.0', '名牌背包,容量大质量好!!!!', null, '2015-02-06 13:23:02');
用户表(user):
CREATE TABLE `user` (
`uid` int(11) NOT NULL auto_increment,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` date default NULL COMMENT '生日',
`sex` char(1) default NULL COMMENT '性别',
`address` varchar(256) default NULL COMMENT '地址',
PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
用户表插入数据:
INSERT INTO `user` VALUES ('1','王五',NULL,'男',NULL);
INSERT INTO `user` VALUES ('10','张三','2014-07-10','女','北京市');
INSERT INTO `user` VALUES ('16','张小明',NULL,'男','河南郑州');
INSERT INTO `user` VALUES ('22','陈小明',NULL,'女','河南郑州');
INSERT INTO `user` VALUES ('24','张三丰',NULL,'男','河南郑州');
INSERT INTO `user` VALUES ('25','陈小明',NULL,'男','河南郑州');
INSERT INTO `user` VALUES ('26', '王五', null, null, null);
INSERT INTO `user` VALUES ('28', '赵四', '2017-05-03', '男', '辽宁');
INSERT INTO `user` VALUES ('29', '小灰灰', '2017-05-03', '女', '西安');
订单表(orders):
CREATE TABLE `orders` (
`oid` int(11) NOT NULL auto_increment,
`user_id` int(11) NOT NULL COMMENT '下单用户id',
`number` varchar(32) NOT NULL COMMENT '订单号',
`createtime` datetime NOT NULL COMMENT '创建订单时间',
`note` varchar(100) default NULL COMMENT '备注',
PRIMARY KEY (`oid`),
KEY `FK_order_1` (`user_id`),
CONSTRAINT `FK_order_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`uid`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
订单表插入数据:
INSERT INTO `orders` VALUES ('3', '1', '1000010', '2015-02-04 13:22:35', null);
INSERT INTO `orders` VALUES ('4', '1', '1000011', '2015-02-03 13:22:41', null);
INSERT INTO `orders` VALUES ('5', '10', '1000012', '2015-02-12 16:13:23', null);
订单明细表(orderdetail):
CREATE TABLE `orderdetail` (
`odid` int(11) NOT NULL auto_increment,
`order_id` int(11) NOT NULL COMMENT '订单id',
`items_id` int(11) NOT NULL COMMENT '商品id',
`items_num` int(11) default NULL COMMENT '商品购买数量',
PRIMARY KEY (`odid`),
KEY `FK_orderdetail_1` (`order_id`),
KEY `FK_orderdetail_2` (`items_id`),
CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`order_id`) REFERENCES `orders` (`oid`),
CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`items_id`) REFERENCES `items` (`iid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
订单明细表插入数据:
INSERT INTO `orderdetail` VALUES ('1', '3', '1', '1');
INSERT INTO `orderdetail` VALUES ('2', '3', '2', '3');
INSERT INTO `orderdetail` VALUES ('3', '4', '3', '4');
INSERT INTO `orderdetail` VALUES ('4', '4', '2', '3');
1.sql语句
select orders.*,user.* from order, user where orders.user_id = user.uid
2.创建POJO(输出类型的包装类)
public class OrdersCustom extends Order {
// 下面添加用户属性
private String uname;
private String sex;
private String address;
// 省略 get 和 set 方法
}
3.UserMapper.xml:
<select id="findOrdersUser" resultType="OrdersCustom">
select orders.*,user.* from orders, user where orders.user_id = user.uid
select>
UserMapper接口:
// 查询订单, 关联查询用户信息
public List<OrdersCustom> findOrderUser() throws Exception;
<resultMap type="Order" id="OrderUserResultMap">
<!– column:表中的字段 property:javabean中字段 -->
<id column="oid" property="oid"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<association property="user" javaType="User">
<id column= "uid" property= "uid"/>
<result column="uname" property="uname"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
association>
resultMap>
resultType和resultMap
订单和订单明细是一对多的关系
orders是主表, orderdetail是关联表
sql语句:
select orders.*,user.*,orderdetail.* from orders,user,orderdetail where orders.user_id=user.uid and orderdetail.order_id=orders.oid
定义resultMap:
<resultMap type="Order" id="OrdersAndOrderDetailResultMap"
extends="OrderUserResultMap">
<collection property="orderDetails" ofType="Orderdetail">
<id column="odid" property= "odid" />
<result column="items_id" property="itemsId" />
<result column="items_num" property="itemsNum" />
<result column="orders_id" property="ordersId" />
collection>
resultMap>
多对多查询:
用户和商品是多对多的关系
由user表开始,通过orders表和orderdetail表,关联items表,具体如下:
user --> orders --> orderdetail --> items
sql语句:
select orders.*,user.*,orderdetail.*,items.*
from orders,user,orderdetail,items
where orders.user_id=user.uid and
orders.oid=orderdetail.orders_id and
orderdetail.items_id=items.id
定义resultMap:
<resultMap type="User" id="UserAndItemsResultMap">
<id column="uid" property="uid"/>
<result column="uname" property="uname"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<collection property="ordersList" ofType="Order">
<id column="oid" property="oid"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<collection property="orderdetails" ofType="Orderdetail">
<id column="odid" property="odid"/>
<result column="items_id" property="itemsId"/>
<result column="items_num" property="itemsNum"/>
<result column="orders_id" property="ordersId"/>
<association property="items" javaType="Items">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="detail" property="detail"/>
<result column="price" property="price"/>
<result column="createtime" property="createtime"/>
association>
collection>
collection>
resultMap>
什么是延迟加载?
举例: 如果查询订单并且关联查询用户信息,先查询订单信息即可满足要求,当我们需要用户信息时再查询用户信息。 对用户信息的按需查询就是延迟加载。所以延迟加载即先从单表查询、 需要时再从关联表去关联查询, 大大提高数据库性能, 因为查询单表要比关联查询多张表速度要快
关联查询:
select orders., user.uname from orders, user where orders.user_id = user.uid
延迟加载相当于:
select orders.,(select uname from user where orders.user_id = user.id) username from orders 我们把关联查询分两次来做, 而不是一次性查出所有的。 第一步只查询单表 orders, 必然会查出 orders中的一个 user_id 字段, 然后我们再根据这个 user_id 查 user 表, 也是单表查询
resultMap结合association和collection可以实现高级映射,其实 association 和 collection 还具备延迟加载的功能。
延迟加载要查询两次, 第二次是按需查询, 之前一对一关联查询的时候只需要查一次, 把订单和用户信息都查出来了, 所以只要写一个 mapper 即可,但是延迟加载要有两个 mapper。
查询订单:
<select id="findOrderUserLazyLoading" resultMap="orderUserLazyLoadingResultMap">
select * from orders
</select>
查询用户:
<select id="findUserById" parameterType="int" resultType="user">
select * from user where uid = #{uid}
</select>
定义resultMap:
<resultMap type="Order" id="orderUserLazyLoadingResultMap">
<id column="oid" property= "oid" />
<result column="user_id" property="userId" />
<result column="number" property="number" />
<result column="createtime" property="createtime" />
<result column="note" property="note" />
<association property="user" javaType="User" select="com.wdzl.mybatis.dao.OrderUserMapper.findUserById" column="user_id">
association>
resultMap>
SqlConfig.xml配置延迟加载:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
settings>
mapper.java
public interface OrderUserMapper {
// 查询订单,关联查询用户信息
public List<Order> findOrderUserLazyLoading();
}
junit测试:
@Test
public void testFindOrderUserLazyLoading() {
SqlSession sqlSession = ssf.openSession();
OrderUserMapper userMapper = sqlSession.getMapper(OrderUserMapper.class);
List<Order> orders = userMapper.findOrderUserLazyLoading();
for(Order order: orders) {
User user = order.getUser();
System.out.println(user);
}
}
缓存的作用是减轻数据库的压力, 提高数据库的性能。 mybatis 中提供了一级缓存和二级缓存
① 一级缓存是 SqlSession 级别的,在操作数据库时需要构造 sqlSession 对象, 在对象中有一个数据结构(HashMap) 用于存储缓存数据。 不同的 sqlSession 之间的缓存数据区域(HashMap) 是互相不影响的。
② 二级缓存是 mapper 级别的, 多个 SqlSession 去操作同一个 Mapper 的 sql 语句, 多个SqlSession 可以共用二级缓存, 二级缓存是跨 SqlSession 的。
原理解析:
① 第一次发起查询 id 为 1 的用户信息, 先去找缓存中是否有 id为1的用户信息, 如果没有, 从数据库查询用户信息。 得到用户信息, 将用户信息存储到一级缓存中
② 如果 sqlSession 执行 commit 操作(插入、 更新、 删除之后), 则会清空SqlSession 中的一级缓存, 这样做的目的为了让缓存中存储的是最新的信息, 避免脏读。
③ 第二次发起查询用户 id 为 1 的用户信息, 先去找缓存中是否有 id 为 1 的用户信息,缓存中有, 直接从缓存中获取用户信息。
一级缓存测试:
public void testFindUserById() {
………….
User user1 = userMapper.findUserById(1);
System.out.println(user1); // 查询用户
User user2 = userMapper.findUserById(1);
System.out.println(user2); // 查询用户
User user = new User("abc",new Date(), "china");
userMapper.insertUser(user); // 添加用户
sqlSession.commit();
User user3 = userMapper.findUserById(1);
System.out.println(user3); // 查询用户
}
二级缓存原理:
① mybatis 中的二级缓存是 mapper 级别的缓存, 不同的 mapper 都有一个二级缓存, 也就是说, 不同的 mapper 之间的二级缓存是互不影响的。
② sqlSession1去查询用户id为1的用户信息, 会将查询数据存储到该UserMapper的二级缓存中。sqlSession2去查询用户id为1的用户信息, 去缓存中找是否存在数据, 如果存在直接从缓存中取出数据。SqlSession3去执行相同mapper下sql, 执行commit提交, 则会清空该 UserMapper 下二级缓存区域的数据。
③ 不同 mapper 是由 namespace 来区分的, 也就是说, 如果两个 mapper 的 namespace 相同, 即使是两个 mapper, 那么这两个 mapper 中执行 sql 查询到的数据也将存在相同的二级缓存区域中。
二级缓存的使用:
SqlConfig.xml中开启二级缓存:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<setting name="cacheEnabled" value="true"/>
settings>
mapper.xml:
<mapper namespace="com.wdzl.mybatis.dao.OrderUserMapper">
<cache>cache>
……….
mapper>
解析:
mapper 中仅仅就一个标签, 并没有配置什么东西, 这是因为mybatis中有默认的实现, 我们如果不配置, 那么就默认使用那个默认的实现。
pojo类实现Serializable接口:
public class User implements Serializable{
………..
}
解析:
pojo类实现 Serializable 接口, 是为了将缓存数据取出执行反序列化操作, 因为二级缓存数据存储介质多种多样, 不一定只存在内存中, 有可能存在硬盘中, 如果我们要再取这个缓存的话, 就需要反序列化了。 所以建议 mybatis中的 pojo 都去实现 Serializable 接口。
二级缓存测试:
public void testFindUserById1() {
// 分别获取sqlSession1、sqlSession2、sqlSession3
User user1 = userMapper1.findUserById(1); // 查询用户
System.out.println(user1);
sqlSession1.close(); // 执行关闭操作, 将数据写到二级缓存区域
User user2 = userMapper2.findUserById(1); // 查询用户
System.out.println(user2);
User user = new User("xyz",new Date(), "china");
userMapper3.insertUser(user); // 添加用户
sqlSession3.commit();
sqlSession3.close();
User user3 = userMapper2.findUserById(1); // 查询用户
System.out.println(user3);
}