mybatis之前的操作

2021/11/01 mybatis 共 25136 字,约 72 分钟
闷骚的程序员

1. 什么是DAO模式?

所谓的dao模式其实无非就是将数据库相关的CRUD操作封装在一个dao接口中,底层操作的仍旧是jdbc。
即DAO模式对外直接提供进行CRUD的各个数据库级别的操作接口.
注意原生jdbc也能实现dao模式。
但是原生 jdbc 实现dao的扩展性不高,原因就是下述的几点,所以本项目中接下来会直接使用mybatis框架实现dao模式。
jdbc编程的缺点:
1、每次操作数据库都要经历加载驱动、建立连接等步骤,频繁开启和关闭数据库连接浪费了数据库的资源(即每次都完整执行”加建创执处关”这6个步骤)。
设想:通过数据源(连接池)进行管理,连接池一般都是底层框架实现好的(言外之意就是数据源信息配置到文件(.xml或者.properties)中或者配置到JNDI中);
2、每次的sql,输入参数(占位符号)和输出参数(输出结果集)都被硬编码(写死在代码中),不灵活,改动sql还得重新编译java源文件。
设想:将sql的编写,输入参数和输出参数的编写和类型的映射关系通过xml文件或者注解的方式进行配置化管理。

2. 原生jdbc编写CRUD

2.1 创建后续整个mybatis所需要的实体类

>我们先创建整个mybatis案例中所需要的所有实体类,后面整个mybatis的介绍用使用的实体类都来自下面。

zeh.mybatis.code00common.entity.ZehPerson:

package zeh.mybatis.code00common.entity;

import java.util.Date;

public class ZehPerson {
    private Long id;

    private String name;

    private Long age;

    private String sex;

    private Date birthday;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public Long getAge() {
        return age;
    }

    public void setAge(Long age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex == null ? null : sex.trim();
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

zeh.mybatis.code00common.entity.ZehUser:

package zeh.mybatis.code00common.entity;

public class ZehUser {
    private Long id;

    private String name;

    private Long age;

    private String sex;

    private String hobby;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public Long getAge() {
        return age;
    }

    public void setAge(Long age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex == null ? null : sex.trim();
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby == null ? null : hobby.trim();
    }
}

基于单表查询的实体类: zeh.mybatis.code00common.po.singleton.CommonPo

package zeh.mybatis.code00common.po.singleton;

import java.io.Serializable;
import java.util.List;

/**
 * 定义pojo:
 * (1)、建议和指定的表结构中的字段和类型保持一致,不能多也不能少。
 * (2)、实际上,pojo实体不必要和表保持一致。但建议保持一致,如果要扩展属性,则新建一个子类作为po类继承原有实体即可。
 * 原理是:对于要执行的sql(crud),需要的字段只需要和parameterType、resultType或者resultMap中规定的保持一致即可。
 * (3)、在执行对应的 statementId 时,如果执行sql需要的字段和parameterType、resultType或者resultMap中依赖的pojo中不一致,将无法正确映射表字段和po对象实体间的关系。
 * (4)、实体pojo和对应表之间的映射是通过parameterType/resultType/resultMap属性进行确定的,所以实体类名不用和表名称保持一致,会根据配置
 * 的parameterType/resultType/resultMap自动将入参和结果集映射成对应pojo中的对应字段。
 * (5)、resultType默认采用mybatis底层执行的规则(表字段名和属性名采用驼峰,自动去掉_)进行自动映射,如果pojo和表字段需要手动映射,需要采取两种方式之一:
 * a、在mapper.xml映射文件中定义resultMap将表中查询出来的字段名和pojo中的属性名称进行映射。
 * b、在执行sql时,将查询出来的表字段名使用 AS 起个别名,别名对应pojo中属性名。
 * (6)、resultType其实本身也是一种resultMap,只不过其查询出来的字段值(本身是存放在List<Map<String,Object>>)会自动映射成pojo中的成员变量。
 * <p>
 * 该po对象对应的物理表是:ZEH_USER
 *
 * @author zhaoeh
 */
public class CommonPo implements Serializable {

    //mybatis的二级缓存,其中resultType指定的类型对应的pojo类必须实现序列化接口,表示该查询结果可以被二级缓存进行序列化
    private static final long serialVersionUID = 1L;

    private int id;
    private String name;
    private int age;
    private String sex;
    //删除 hobby 的getter方法,证明 #{}和${}取值原则:优先按照 getXXX 或者 isXXX 反射 XXX 取值;如果没有getXXX 或者 isXXX,则直接反射对应的属性名即 hobby。
    private String hobby;

    //自定义属性,表中不存在的字段属性,完全不影响和表字段之间的映射关系。
    private String testProperty;
    private List<String> allList;

    //自定义整数类型的动态表名后缀,验证是否在insert语句中传入动态表名参数总是报错
    private Integer tableName;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    // getSex()方法手动指定返回值,则优先取该值。
    public String getSex() {
        return "不男不女";
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    public String getTestProperty() {
        return testProperty;
    }

    public void setTestProperty(String testProperty) {
        this.testProperty = testProperty;
    }

    public List<String> getAllList() {
        return allList;
    }

    public void setAllList(List<String> allList) {
        this.allList = allList;
    }

    //isZhao()和getZhao()方法同一个pojo中只能定义一个,否则mybatis在反射的时候不知道该反射哪一个方法。
//    public boolean getZhao() {
//        return true;
//    }

    /**
     * 定义isXXX()方法,验证 if 标签的boolean表达式是根据XXX反射的isXXX方法,而不是反射的属性。
     * <p>
     * 结论:是优先根据XXX反射isXXX()或者getXXX()得到返回值,没有对应的getXXX()或者isXXX()方法才反射属性。
     *
     * @return
     */
    public boolean isZhao() {
        return true;
    }

    /**
     * 定义getXXX()方法,验证 if 标签的boolean表达式是根据XXX反射的getXXX方法,而不是反射的属性。
     * <p>
     * 结论:是优先根据XXX反射isXXX()或者getXXX()得到返回值,没有对应的getXXX()或者isXXX()方法才反射属性。
     *
     * @return
     */
    public boolean getYui() {
        return true;
    }

    /**
     * 不定义为成员而是直接写一个getXXX()方法。
     * 验证mybatis 的 #{} 和${}都是优先根据XXX反射的getXXX()或者isXXX()方法,没有getXXX()方法才反射的属性。
     * 结论:是优先根据XXX反射isXXX()或者getXXX()得到返回值,没有对应的getXXX()或者isXXX()方法才反射属性。
     *
     * @return
     */
    public String getMyName() {
        return "getMyName";
    }

    /**
     * 不定义为成员而是直接写一个isXXX()方法。
     * <p>
     * 验证mybatis 的 #{} 和${}都是根据XXX反射的getXXX()或者isXXX()方法而不是反射的属性。
     * <p>
     * 结论:是优先根据XXX反射isXXX()或者getXXX()得到返回值,没有对应的getXXX()或者isXXX()方法才反射属性。
     *
     * @return
     */
    public String isUserName() {
        return "isUserName";
    }


    public Integer getTableName() {
        return tableName;
    }

    public void setTableName(Integer tableName) {
        this.tableName = tableName;
    }

    @Override
    public String toString() {
        return "[id = " + this.getId() + ";name = " + this.getName() + ";age = " + this.getAge() + ";sex = " + this.getSex() + ";hobby = " + this.hobby + "]";
    }
}

zeh.mybatis.code00common.po.singleton.UserCustomVo

package zeh.mybatis.code00common.po.singleton;

import java.io.Serializable;

/**
 * 功能描述:
 * (1)、po类的扩展类。
 * (2)、一般要定义vo类引用po类时,最好不要修改原来的po类,而是重新扩展出一个新的子类对原来的vo进行补充。
 * 因为有可能原来的vo类已经被很多其他类引用,如果不扩展子类将会引起高耦合。
 *
 * @params:
 * @return:
 * @author: zhaoerhu
 * @date: 2019/9/6 16:15
 */
public class UserCustomVo extends CommonPo implements Serializable {

    private static final long serialVersionUID = 1L;

    // 可以扩展用户的信息
}

zeh.mybatis.code00common.po.singleton.UserQueryVo

package zeh.mybatis.code00common.po.singleton;

import java.util.List;

/**
 * 定义vo类:对简单po对象再次封装的类就是vo类,即向vo类中委托注入其他的po类。
 *
 * @author zhaoeh
 */
public class UserQueryVo {

    // 传入多个id
    private List<Integer> ids;

    // 在这里包装所需要的查询条件

    // 用户查询条件
    private UserCustomVo userCustom;

    public UserCustomVo getUserCustom() {
        return userCustom;
    }

    public void setUserCustom(UserCustomVo userCustom) {
        this.userCustom = userCustom;
    }

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }

    // 可以包装其它的查询条件,订单、商品
    // ....

}

zeh.mybatis.code00common.po.singleton.ZehPersonPo

package zeh.mybatis.code00common.po.singleton;

import java.util.Date;

/**
 * 功能描述:
 * ZehPerson实体类,对应表是zeh_person
 *
 * @ClassName ZehPersonPo
 * @Author zhaoerhu
 * @Date 2019/7/23 11:18
 */
public class ZehPersonPo {
    private int id;
    private String name;
    private int age;
    private String sex;
    private Date birthday;
    private String testProperty;

    //动态表名参数后缀
    private Integer tableName;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    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 getTestProperty() {
        return testProperty;
    }

    public void setTestProperty(String testProperty) {
        this.testProperty = testProperty;
    }

    public Integer getTableName() {
        return tableName;
    }

    public void setTableName(Integer tableName) {
        this.tableName = tableName;
    }
}

定义级联多表查询的实体类: zeh.mybatis.code00common.po.more.MarshallUser

package zeh.mybatis.code00common.po.more;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

/**
 * 用户信息po类。
 * 必须和指定的表结构中的字段和类型保持一致,否则将无法正确映射表字段和po对象实体间的关系
 *
 * @类名称: User
 * @作者: zhaoerhu
 * @创建时间: 2019-4-5
 * @修改时间: 2019-4-5上午9:29:04
 * @备注:尊重每一行代码!
 */
public class MarshallUser implements Serializable {
    private static final long serialVersionUID = 1950306035918930917L;
    private int id;
    private String username;// 用户姓名
    private String sex;// 性别
    private Date birthday;// 生日
    private String address;// 地址

    // 一个用户可能具备多个订单。用于多对多查询结果的实体依赖。
    private List<MarshallOrders> orders;

    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;
    }

    public List<MarshallOrders> getOrders() {
        return orders;
    }

    public void setOrders(List<MarshallOrders> orders) {
        this.orders = orders;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                ", address='" + address + '\'' +
                ", orders=" + orders +
                '}';
    }
}

zeh.mybatis.code00common.po.more.MarshallCustom

package zeh.mybatis.code00common.po.more;

/**
 * po类的扩展类
 * 
 * 一般要定义vo类引用po类时,最好不要修改原来的po类,而是重新扩展出一个新的子类对原来的vo进行补充
 * 
 * 因为有可能原来的vo类已经被很多其他类引用,如果不扩展子类将会引起高耦合。
 * 
 * @author zhaoeh
 * 
 */
public class MarshallCustom extends MarshallUser {

	// 可以扩展用户的信息

}

zeh.mybatis.code00common.po.more.MarshallUserCustom

package zeh.mybatis.code00common.po.more;

import java.util.Date;

/**
 * 用户信息的扩展vo。
 *
 * @类名称: UserCustomVo
 * @作者: zhaoerhu
 * @创建时间: 2019-4-5
 * @修改时间: 2019-4-5上午9:30:30
 * @备注:尊重每一行代码!
 */
public class MarshallUserCustom extends MarshallUser {

    private static final long serialVersionUID = 3900305244996910319L;
    // 这里添加扩展字段
    private Date birthday_start;// 起始日期
    private Date birthday_end;// 截止日期

    public Date getBirthday_start() {
        return birthday_start;
    }

    public void setBirthday_start(Date birthday_start) {
        this.birthday_start = birthday_start;
    }

    public Date getBirthday_end() {
        return birthday_end;
    }

    public void setBirthday_end(Date birthday_end) {
        this.birthday_end = birthday_end;
    }

}

zeh.mybatis.code00common.po.more.MarshallItems

package zeh.mybatis.code00common.po.more;

import java.util.Date;

/**
 * 该包下的po和vo都是基于一对多查询、多对多查询的复杂查询的po类。
 * <p>
 * 一对多或者多对多的复杂查询和一对一的简单查询其实对于po类没有什么要求,各个po类之间几乎不需要彼此依赖。
 * 对于一对多或者多对多的查询结果映射,一般存在两种方式:
 * (1)、自己再独立定义一个po类来完成结果到po的映射,这个po可以通过继承其他po的方式;(注意查询结果映射的po往往里面保存的是简单类型)。
 * (2)、如果不想独立定义一个po类,就想使用原有的po,那么可以在mapper.xml中通过associaction标签来映射复杂查询结果中的其他po的映射。
 * 永远注意:不论哪种方式,对于查询结果的映射po,几乎不需要定义po之间的委托依赖关系,因为查询结果往往都是简单类型的。
 * <p>
 * 商品po类。
 *
 * @类名称: Items
 * @作者: zhaoerhu
 * @创建时间: 2019-4-5
 * @修改时间: 2019-4-5上午9:26:23
 * @备注:尊重每一行代码!
 */
public class MarshallItems {
    private Integer id;

    private String name;

    private Float price;

    private String pic;

    private Date createtime;

    private String detail;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public Float getPrice() {
        return price;
    }

    public void setPrice(Float price) {
        this.price = price;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic == null ? null : pic.trim();
    }

    public Date getCreatetime() {
        return createtime;
    }

    public void setCreatetime(Date createtime) {
        this.createtime = createtime;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail == null ? null : detail.trim();
    }

    @Override
    public String toString() {
        return "MarshallItems{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", pic='" + pic + '\'' +
                ", createtime=" + createtime +
                ", detail='" + detail + '\'' +
                '}';
    }
}

zeh.mybatis.code00common.po.more.MarshallOrders

package zeh.mybatis.code00common.po.more;

import java.util.Date;
import java.util.List;

/**
 * 订单po类。
 *
 * @类名称: Orders
 * @作者: zhaoerhu
 * @创建时间: 2019-4-5
 * @修改时间: 2019-4-5上午9:26:52
 * @备注:尊重每一行代码!
 */
public class MarshallOrders {
    private Integer id;

    private Integer userId;

    private String number;

    private Date createtime;

    private String note;

    // 用户信息,一对一查询,用于使用resultMap进行关联查询时,必须要依赖的购买用户实体对象。
    private MarshallUser user;

    // 订单明细,一对多查询,用于使用resultMap进行关联查询时,必须要依赖的订单明细实体对象。
    private List<MarshallOrderDetail> orderdetails;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number == null ? null : number.trim();
    }

    public Date getCreatetime() {
        return createtime;
    }

    public void setCreatetime(Date createtime) {
        this.createtime = createtime;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note == null ? null : note.trim();
    }

    public MarshallUser getUser() {
        return user;
    }

    public void setUser(MarshallUser user) {
        this.user = user;
    }

    public List<MarshallOrderDetail> getOrderdetails() {
        return orderdetails;
    }

    public void setOrderdetails(List<MarshallOrderDetail> orderdetails) {
        this.orderdetails = orderdetails;
    }

    @Override
    public String toString() {
        return "MarshallOrders{" +
                "id=" + id +
                ", userId=" + userId +
                ", number='" + number + '\'' +
                ", createtime=" + createtime +
                ", note='" + note + '\'' +
                ", user=" + user +
                ", orderdetails=" + orderdetails +
                '}';
    }
}

zeh.mybatis.code00common.po.more.MarshallOrdersCustom

package zeh.mybatis.code00common.po.more;

/**
 * 订单自定义po,包括订单信息、用户信息。
 * <p>
 * 这种po通过继承的方式,相当于单独定义了一种返回结果,而没有采用对象之间的委托依赖关系。
 *
 * @类名称: OrdersCustom
 * @作者: zhaoerhu
 * @创建时间: 2019-4-5
 * @修改时间: 2019-4-5上午9:28:10
 * @备注:尊重每一行代码!
 */
public class MarshallOrdersCustom extends MarshallOrders {

    private String username;// 用户名称
    private String address;// 用户地址
    private String sex; // 用户性别

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

zeh.mybatis.code00common.po.more.MarshallOrderDetail

package zeh.mybatis.code00common.po.more;

/**
 * 订单明细po类。
 *
 * @类名称: Orderdetail
 * @作者: zhaoerhu
 * @创建时间: 2019-4-5
 * @修改时间: 2019-4-5上午9:26:41
 * @备注:尊重每一行代码!
 */
public class MarshallOrderDetail {
    private Integer id;

    private Integer ordersId;

    private Integer itemsId;

    private Integer itemsNum;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getOrdersId() {
        return ordersId;
    }

    public void setOrdersId(Integer ordersId) {
        this.ordersId = ordersId;
    }

    public Integer getItemsId() {
        return itemsId;
    }

    public void setItemsId(Integer itemsId) {
        this.itemsId = itemsId;
    }

    public Integer getItemsNum() {
        return itemsNum;
    }

    public void setItemsNum(Integer itemsNum) {
        this.itemsNum = itemsNum;
    }

    @Override
    public String toString() {
        return "MarshallOrderDetail{" +
                "id=" + id +
                ", ordersId=" + ordersId +
                ", itemsId=" + itemsId +
                ", itemsNum=" + itemsNum +
                '}';
    }
}

zeh.mybatis.code00common.po.more.MarshallQueryVo

package zeh.mybatis.code00common.po.more;

import java.util.List;

/**
 * 对于复杂查询条件进行封装,使用对象之间的委托依赖实现复杂查询参数的封装。
 *
 * @类名称: QueryVo
 * @作者: zhaoerhu
 * @创建时间: 2019-4-5
 * @修改时间: 2019-4-5上午9:28:20
 * @备注:尊重每一行代码!
 */
public class MarshallQueryVo {

    private MarshallUser user;

    // 自定义用户扩展类
    private MarshallUserCustom userCustom;

    // 传递多个用户id
    private List<Integer> ids;

    public MarshallUser getUser() {
        return user;
    }

    public void setUser(MarshallUser user) {
        this.user = user;
    }

    public MarshallUserCustom getUserCustom() {
        return userCustom;
    }

    public void setUserCustom(MarshallUserCustom userCustom) {
        this.userCustom = userCustom;
    }

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }

}

工具类: zeh.mybatis.code00common.utils.UUIDUtils

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package zeh.mybatis.code00common.utils;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * UUID的工具类
 *
 * @类名称: UUIDUtils
 * @作者: zhaoerhu
 * @创建时间: 2019-4-29
 * @修改时间: 2019-4-29下午9:02:35
 * @备注:尊重每一行代码!
 */
public class UUIDUtils {
    static Logger logger = LoggerFactory.getLogger(UUIDUtils.class);
    private static SecureRandom secureRandom = null;
    private static String netAddressHexStr = null;
    private static String portHexStr = "0000";
    private static byte[] addressBytes = null;
    private static int nextSeq32767 = 0;
    private static int nextSeq999 = 0;
    private static char[] radixDigits = new char[]{'0', '1', '2', '3', '4',
            '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
            'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
            'V', 'W', 'X', 'Y', 'Z'};

    private UUIDUtils() {
    }

    public static String getStringValue() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    public static String generate() {
        StringBuffer uid = new StringBuffer(32);
        uid.append(getSystemMillisRadix32());
        uid.append(getSeqRadix32());
        uid.append(obtainNetAddressHexStr());
        uid.append(portHexStr);
        uid.append(toHex(getRandom(), 8));
        return uid.toString();
    }

    public static long getUniqueLong() {
        long longValue = System.currentTimeMillis();
        longValue *= 1000000L;
        long ipv4_lastNumber = (long) ((obtainAddressBytes()[3] & 255) * 1000);
        longValue = longValue + ipv4_lastNumber + (long) getSeq999();
        return longValue;
    }

    private static String getSystemMillisRadix32() {
        String millisStr = Long.toString(System.currentTimeMillis(), 32)
                .toUpperCase();
        int len = millisStr.length();
        if (len < 9) {
            StringBuffer buffer = new StringBuffer(10);
            buffer.append(millisStr);
            int offset = 9 - len;

            for (int i = 0; i < offset; ++i) {
                buffer.append("0");
            }

            millisStr = buffer.toString();
        } else if (len > 9) {
            millisStr = millisStr.substring(len - 9);
        }

        return millisStr;
    }

    private static synchronized int getRandom() {
        if (secureRandom == null) {
            secureRandom = new SecureRandom();
        }

        return secureRandom.nextInt();
    }

    private static String toHex(int value, int length) {
        StringBuffer buffer = new StringBuffer(length);
        int shift = length - 1 << 2;
        int kk = -1;

        while (true) {
            ++kk;
            if (kk >= length) {
                return buffer.toString();
            }
            buffer.append(radixDigits[value >> shift & 15]);
            value <<= 4;
        }
    }

    private static int toInt(byte[] bytes) {
        int value = 0;
        int aryLen = bytes.length;

        for (int i = aryLen - 1; i >= 0; --i) {
            value <<= 8;
            value |= bytes[i] & 255;
        }
        return value;
    }

    private static synchronized int getSeq32767() {
        int retv = nextSeq32767++;
        if (nextSeq32767 >= 32768) {
            nextSeq32767 = 0;
        }
        return retv;
    }

    private static String getSeqRadix32() {
        String seqStr = Long.toString((long) getSeq32767(), 32).toUpperCase();
        int len = seqStr.length();
        if (len < 3) {
            StringBuffer buffer = new StringBuffer(3);
            int offset = 3 - len;

            for (int i = 0; i < offset; ++i) {
                buffer.append("0");
            }

            buffer.append(seqStr);
            seqStr = buffer.toString();
        } else if (len > 3) {
            seqStr = seqStr.substring(len - 3);
        }

        return seqStr;
    }

    private static synchronized int getSeq999() {
        int retValue = nextSeq999++;
        if (nextSeq999 >= 1000) {
            nextSeq999 = 0;
        }
        return retValue;
    }

    private static byte[] obtainAddressBytes() {
        if (addressBytes == null) {
            try {
                addressBytes = InetAddress.getLocalHost().getAddress();
            } catch (UnknownHostException var1) {
                logger.error("获取当前机器 host 异常:" + var1.getMessage());
            }
        }
        return addressBytes;
    }

    private static String obtainNetAddressHexStr() {
        if (netAddressHexStr == null) {
            netAddressHexStr = toHex(toInt(obtainAddressBytes()), 8);
        }

        return netAddressHexStr;
    }
}

zeh.mybatis.code00common.utils.PageInfoLimitUtil

package zeh.mybatis.code00common.utils;

import com.github.pagehelper.PageInfo;

import java.util.List;

/**
 * 功能描述:手动分页工具类
 *
 * @ClassName PageInfoLimit
 * @Author zhaoerhu
 * @Date 2020/3/27 11:12
 */
public class PageInfoLimitUtil {
    /**
     * 功能描述: 抽取方法,手动封装PageInfo参数。
     *
     * @params: []
     * @return: void
     * @author: zhaoerhu
     * @date: 2020/2/24 18:31
     */
    public static void pageInfoLimit(List list, PageInfo pageInfo, int pageNumParam, int pageSizeParam) {
        //手动设置PageInfo参数,进行手动分页
        int start = 0;
        int end = 0;
        int totalPages = 0;
        int totalRecord = 0;
        int pageSize = 0;
        int size = 0;
        int number = 0;
        size = pageSizeParam;
        number = pageNumParam;
        pageSize = pageSizeParam;
        totalRecord = list.size();
        //设置总数
        pageInfo.setTotal(totalRecord);
        //设置每页的显示条数
        pageInfo.setPageSize(size);
        //设置要显示的是第几页的数据
        pageInfo.setPageNum(number);
        pageInfo.setSize(totalRecord);
        //计算获取对应的要显示的数据
        if (totalRecord % pageSize == 0) {
            totalPages = totalRecord / pageSize;
        } else {
            totalPages = totalRecord / pageSize + 1;
        }
        pageInfo.setPages(totalPages);
        //初始边界值计算
        if (number == 1) {
            start = 0;
            pageInfo.setHasPreviousPage(false);
            pageInfo.setPrePage(0);
            pageInfo.setIsFirstPage(true);
        } else {
            start = pageInfo.getPageSize() * (pageInfo.getPageNum() - 1);
            pageInfo.setHasPreviousPage(true);
            pageInfo.setPrePage(number - 1);
            pageInfo.setIsFirstPage(false);
        }
        pageInfo.setStartRow((number - 1) * pageSize);
        //结束边界值计算
        if ((start + pageInfo.getPageSize() > pageInfo.getTotal())) {
            end = totalRecord;
            pageInfo.setHasNextPage(false);
            pageInfo.setIsLastPage(true);
            pageInfo.setEndRow(totalRecord);
        } else {
            end = start + pageInfo.getPageSize();
            pageInfo.setHasNextPage(true);
            pageInfo.setNextPage(number + 1);
            pageInfo.setIsLastPage(false);
            pageInfo.setEndRow((number) * pageSize);
        }
        if (start < end && end <= totalRecord) {
            pageInfo.setList(list.subList(start, end));
        }
        if (pageInfo.getSize() == 0) {
            pageInfo.setStartRow(0);
            pageInfo.setEndRow(0);
        } else {
            pageInfo.setStartRow(pageInfo.getStartRow() + 1);
            pageInfo.setEndRow(pageInfo.getStartRow() - 1 + pageInfo.getSize());
        }
        pageInfo.setPages(totalPages);
        pageInfo.setNavigateLastPage(totalPages > number ? number + 1 : totalPages);
    }

    /**
     * 功能描述: 重载手动分页方法
     *
     * @params: [pageInfo, pageNumParam, pageSizeParam]
     * @return: void
     * @author: zhaoerhu
     * @date: 2020/2/24 22:03
     */
    public static void pageInfoLimit(PageInfo pageInfo, int pageNumParam, int pageSizeParam) {
        List list = pageInfo.getList();
        //手动设置PageInfo参数,进行手动分页
        int pageSize = 0;
        int size = 0;
        int number = 0;
        int start = 0;
        int end = 0;
        int totalPages = 0;
        int totalRecord = 0;

        size = pageSizeParam;
        number = pageNumParam;
        pageSize = pageSizeParam;
        totalRecord = list.size();
        //设置总数
        pageInfo.setTotal(totalRecord);
        //设置每页的显示条数
        pageInfo.setPageSize(size);
        //设置要显示的是第几页的数据
        pageInfo.setPageNum(number);
        pageInfo.setSize(totalRecord);
        //计算获取对应的要显示的数据
        if (totalRecord % pageSize == 0) {
            totalPages = totalRecord / pageSize;
        } else {
            totalPages = totalRecord / pageSize + 1;
        }
        pageInfo.setPages(totalPages);
        //初始边界值计算
        if (number == 1) {
            start = 0;
            pageInfo.setHasPreviousPage(false);
            pageInfo.setPrePage(0);
            pageInfo.setIsFirstPage(true);
        } else {
            start = pageInfo.getPageSize() * (pageInfo.getPageNum() - 1);
            pageInfo.setHasPreviousPage(true);
            pageInfo.setPrePage(number - 1);
            pageInfo.setIsFirstPage(false);
        }
        pageInfo.setStartRow((number - 1) * pageSize);
        //结束边界值计算
        if ((start + pageInfo.getPageSize() > pageInfo.getTotal())) {
            end = totalRecord;
            pageInfo.setHasNextPage(false);
            pageInfo.setIsLastPage(true);
            pageInfo.setEndRow(totalRecord);
        } else {
            end = start + pageInfo.getPageSize();
            pageInfo.setHasNextPage(true);
            pageInfo.setNextPage(number + 1);
            pageInfo.setIsLastPage(false);
            pageInfo.setEndRow((number) * pageSize);
        }
        if (start < end && end <= totalRecord) {
            pageInfo.setList(list.subList(start, end));
        }
        if (pageInfo.getSize() == 0) {
            pageInfo.setStartRow(0);
            pageInfo.setEndRow(0);
        } else {
            pageInfo.setStartRow(pageInfo.getStartRow() + 1);
            pageInfo.setEndRow(pageInfo.getStartRow() - 1 + pageInfo.getSize());
        }
        pageInfo.setPages(totalPages);
        pageInfo.setNavigateLastPage(totalPages > number ? number + 1 : totalPages);
    }
}

2.2 使用jdbc编写CRUD

通过jdbc实现DAO:

package zeh.mybatis.code01jdbc.dao;


import zeh.mybatis.code00common.entity.ZehPerson;

import java.sql.*;
import java.util.Date;

/**
 * 传统的 jdbc 方式连接数据库进行model层的操作-JDBC原生方式实现DAO模式,使用JUnit进行测试。
 * @author zhaoeh
 */

public class JdbcDao {

    /**
     * 功能描述: 原生jdbc实现Dao
     *
     * @params: [isPreparedStatement]
     * @return: void
     * @author: zhaoerhu
     * @date: 2020/4/10 20:21
     */
    public void process(Boolean isPreparedStatement) {
        // 数据库连接
        Connection connection = null;
        // 预编译的Statement,使用预编译的Statement提高数据库性能;
        // 之所以提高性能,就是因为sql语句是通过jdbc进行预编译后直接发送给数据库进行执行的,数据库不再需要进行sql命令的编译。
        // 并且预编译操作采取参数化占位符的方式,能够防止sql注入。
        PreparedStatement preparedStatement = null;
        //非预编译的statement
        Statement statement = null;
        // 结果集
        ResultSet resultSet = null;
        try {
            // 1.加载数据库驱动
            Class.forName("oracle.jdbc.driver.OracleDriver");

            // 2.通过驱动管理类建立数据库连接,输入用户名和密码,相当于最原始的数据源的功能
            connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "oracle", "oracle");


            if (isPreparedStatement) {
                // 预编译
                // 定义sql语句 ?表示占位符,对于占位符的sql语句必须使用 prepareStatement 进行执行,prepareStatement 由jdbc进行预编译然后统一由数据库执行。
                String sql = "SELECT * FROM ZEH_PERSON WHERE NAME = ?";
                // 3.创建预处理statement对象。
                preparedStatement = connection.prepareStatement(sql);
                // 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
                preparedStatement.setString(1, "赵二虎");
                // 4.向数据库发出sql执行查询,查询出结果集
                resultSet = preparedStatement.executeQuery();
            } else {
                // 非预编译
                String queryParam = "\'赵二虎\'";
                String sql = "SELECT * FROM ZEH_PERSON WHERE NAME = " + queryParam;
                // 3.创建非预处理statement对象。非预处理的sql只能提前准备好,不能使用占位符,因为jdbc不预编译,所以准备好sql统一发送给数据库进行编译执行。
                statement = connection.createStatement();
                // 4.向数据库发出sql执行查询,查询出结果集
                resultSet = statement.executeQuery(sql);
            }


            // 5.遍历处理查询结果集 resultSet
            while (resultSet.next()) {
                //可以看出,原生 jdbc 没有使用任何pojo映射和数据表字段之间的关系,而是直接从resultSet结果集中进行捞取的。
                Long id = Long.parseLong(resultSet.getString("id"));
                String name = resultSet.getString("name");
                String sex = resultSet.getString("sex");

                //将数据库中的date类型取出来是string字符串,然后通过SimpleDateFormat类格式化为Java中的Date类型,再取出其中的毫秒数,即自1970年后的毫秒数。
                Date date = resultSet.getDate("birthday");

                //通过原生jdbc从resultSet结果集中捞取到对应的字段值,可以手动将这些值封装到对应的实体类中。模拟ORM框架的映射原理。
                ZehPerson zehPerson = new ZehPerson();
                zehPerson.setId(id);
                zehPerson.setName(name);
                zehPerson.setSex(sex);
                zehPerson.setBirthday(date);

                printZehPerson(zehPerson);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6.关闭资源
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 功能描述: 打印封装好的 zehPerson 对象
     *
     * @params: [zehPerson]
     * @return: void
     * @author: zhaoerhu
     * @date: 2020/4/10 20:44
     */
    private void printZehPerson(ZehPerson zehPerson) {
        /**
         * oracle中的null值和空字符取出来是不是java中的null值?
         * 答案:对于oracle而言,空字符就是null值,null值就是空字符,取出来的就是java中的null值而不是java中的空字符。
         * 总结:oracle中没有空字符一说,存入的空字符就是null值,需要使用 is null 才能查询到。所以 oracle 中的空字符取出就是java中的null值(注意不是空格)。
         */
        String sex = zehPerson.getSex();

        System.out.println("sex == null ? " + (sex == null));
        System.out.println("\"null\".equals(sex) ? " + ("null".equals(sex)));
        System.out.println("\"\".equals(sex) ? " + ("".equals(sex)));
    }
}

执行程序:

package zeh.mybatis.code01jdbc.run;

import org.junit.Before;
import org.junit.Test;
import zeh.mybatis.code01jdbc.dao.JdbcDao;

/**
 * 通过JUnit测试jdbc传统方式操作数据库(即jdk自带的原生jdbc操作)。
 *
 * @类名称: JdbcDaoJunitRun
 * @作者: zhaoerhu
 * @创建时间: 2019-3-6
 * @修改时间: 2019-3-6下午12:45:59
 * @备注:尊重每一行代码!
 */
public class JdbcDaoRun {

    //委托dao对象
    private JdbcDao jdbcDao;

    //@Before是Junit自带的注解,表示在@Test标注的方法前执行。
    @Before
    public void initJdbcDao() {
        this.jdbcDao = new JdbcDao();
    }

    //@Test是Junit自带的注解,选中该方法,右键使用Junit运行,则会自动运行该方法,类似于main方法。
    @Test
    public void testJdbcDaoUsePrepared() {
        jdbcDao.process(Boolean.TRUE);
    }

    @Test
    public void testJdbcDaoNotUsePrepared() {
        jdbcDao.process(Boolean.FALSE);
    }
}

3. jdbc实现DAO的缺点

使用jdbc实现DAO模式,发现其中有大量的重复步骤,即模板化的冗余代码,而且灵活度很低。

文档信息

Search

    Table of Contents