Mybatis基础姿势深入大解析

1. JDBC连接数据库

基础回顾:在咱们的JAVA里面怎么连接数据库?

首先引入MySQL连接驱动maven依赖:

<!-- mysql driver -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
@SpringBootTest
class MyJDBCTest {
	 /**
     * @Description: jdbc test
     * @Author create by mamba
     * @Date 2020/7/7 11:18
     * @param {}
     */
    @Test
    void myJDBC(){
        Connection connection = null;
        Statement statement = null;
        Saying saying = new Saying();
        try{
            // register JDBC Driver
            Class.forName("com.mysql.jdbc.Driver");
            // open connection
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/commentsystem?serverTimezone=UTC", "root", "607918");
            statement = connection.createStatement();
            String sql = "select * from saying where 1=1";
            ResultSet resultSet = statement.executeQuery(sql);
            while (resultSet.next()){
                Integer sayingId = resultSet.getInt("saying_id");
                String author = resultSet.getString("author");
                String sayingContent = resultSet.getString("sayingContent");
                String likes = resultSet.getString("likes");
                Date createTime = resultSet.getDate("createTime");
                saying.setSaying_id(sayingId);
                saying.setAuthor(author);
                saying.setSayingContent(sayingContent);
                saying.setLikes(likes);
                saying.setCreateTime(createTime);
                System.out.println(saying);
            }
            // close sequence 相反跟打开的顺序
            resultSet.close();
            statement.close();
            connection.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
// print result
Saying{saying_id=1, author='huanghedidi', sayingAvatar='null', sayingContent='我喜欢,父亲的散文诗', likes='1,2,3,4,8', createTime=2019-05-06}
Saying{saying_id=2, author='huanghedidi', sayingAvatar='null', sayingContent='second_test', likes='11', createTime=2019-05-03}

思考:当项目复杂庞大时候 直接使用原生的API会带那些问题?
1. 大量的重复性代码,SQL和业务代码耦合度极高, 维护难度很大,不利于项目迭代
2. 加大了出错概率 结果集需要手动处理 加大了开发难度
3. 需要手动管理JDBC资源

2. Spring JDBC

在spring里面对JDBC的封装 是怎么连接数据库?

我们先大体看一下spring的板块图,可见在spring的Data Access/Integration模块包括了对JDBC ORM等进行封装,如果要是用spring封装好的JDBCTemplate,需要引入对应的maven依赖:

 <!-- 引入spring JDBC-->
 <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-jdbc</artifactId>
     <version>${spring.version}</version>
 </dependency>

spring板块模型

思考:引入spring的JDBC之后 我们应该怎么样的访问数据库呢?
其实spring对于Redis MongoDB RabbitMQ等 都提供了对应的template进行封装 简化开发人员操作
1. JDBCTemplate 对于数据库的连接管理 资源管理

2.1 spring JDBC对于结果集的处理

1.实现RowMapper接口,mapRow()方法

public class SayingRowMapper implements RowMapper {
    @Override
    public Object mapRow(ResultSet resultSet, int i) throws SQLException {
        Saying saying = new Saying();
        saying.setSaying_id(resultSet.getInt("saying_id"));
        saying.setAuthor(resultSet.getString("author"));
        saying.setSayingAvatar(resultSet.getString("sayingAvatar"));
        saying.setSayingContent(resultSet.getString("sayingContent"));
        saying.setLikes(resultSet.getString("likes"));
        saying.setCreateTime(resultSet.getDate("createTime"));
        // 因为返回类型是Object 所以即使有多组结果的时候自动进行封装成list 或者 map
        return saying;
    }
}

2.转换结果集返回Object

@Autowired
private JdbcTemplate jdbcTemplate;

private List<Saying> sayings;

@Test
void MyRowMapperTest(){
    sayings = jdbcTemplate.query("select * from saying where 1=1", new SayingRowMapper());
    sayings.forEach(System.out::println);
}

思考: 这样写好像并没有简单多少啊?还是要手动进行类型转换 对象赋值
1. 需要做数据库字段名称和JAVA对象的字段的名称转换(下划线到驼峰命名)
2. 数据库字段类型转换成JAVA的引用类型或基本类型(tinyInt, int, bigint… varchar, char, text, datetime…)
3. 有没有一个自动的映射方法,解决以上的问题?

public class BaseRowMapper<T> implements RowMapper<T> {

    private Class<?> targetClazz;
    private HashMap<String, Field> fieldMap;

    public BaseRowMapper(Class<?> targetClazz) {
        this.targetClazz = targetClazz;
        fieldMap = new HashMap<>();
        Field[] fields = targetClazz.getDeclaredFields();
        for (Field field : fields) {
            fieldMap.put(field.getName(), field);
        }
    }

    @Override
    public T mapRow(ResultSet rs, int arg1) throws SQLException {
        T obj = null;

        try {
            obj = (T) targetClazz.newInstance();

            final ResultSetMetaData metaData = rs.getMetaData();
            int columnLength = metaData.getColumnCount();
            String columnName = null;

            for (int i = 1; i <= columnLength; i++) {
                columnName = metaData.getColumnName(i);
                Class fieldClazz = fieldMap.get(camel(columnName)).getType();
                Field field = fieldMap.get(camel(columnName));
                field.setAccessible(true);

                // fieldClazz == Character.class || fieldClazz == char.class
                if (fieldClazz == int.class || fieldClazz == Integer.class) { // int
                    field.set(obj, rs.getInt(columnName));
                } else if (fieldClazz == boolean.class || fieldClazz == Boolean.class) { // boolean
                    field.set(obj, rs.getBoolean(columnName));
                } else if (fieldClazz == String.class) { // string
                    field.set(obj, rs.getString(columnName));
                } else if (fieldClazz == float.class) { // float
                    field.set(obj, rs.getFloat(columnName));
                } else if (fieldClazz == double.class || fieldClazz == Double.class) { // double
                    field.set(obj, rs.getDouble(columnName));
                } else if (fieldClazz == BigDecimal.class) { // bigdecimal
                    field.set(obj, rs.getBigDecimal(columnName));
                } else if (fieldClazz == short.class || fieldClazz == Short.class) { // short
                    field.set(obj, rs.getShort(columnName));
                } else if (fieldClazz == Date.class) { // date
                    field.set(obj, rs.getDate(columnName));
                } else if (fieldClazz == Timestamp.class) { // timestamp
                    field.set(obj, rs.getTimestamp(columnName));
                } else if (fieldClazz == Long.class || fieldClazz == long.class) { // long
                    field.set(obj, rs.getLong(columnName));
                }

                field.setAccessible(false);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return obj;
    }

    /**
     * 下划线转驼峰
     * @param str
     * @return
     */
    public static String camel(String str) {
        Pattern pattern = Pattern.compile("_(\\w)");
        Matcher matcher = pattern.matcher(str);
        StringBuffer sb = new StringBuffer(str);
        if(matcher.find()) {
            sb = new StringBuffer();
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
            matcher.appendTail(sb);
        }else {
            return sb.toString();
        }
        return camel(sb.toString());
    }
}

有了这个baseMapper之后 我们处理起来就更加方便了

@Test
void MyRowMapperTest(){
    sayings = jdbcTemplate.query("select * from saying where 1=1", new BaseMapper(Saying.class));
    sayings.forEach(System.out::println);
}

思考:这样还是有一些问题没有解决啊
1. SQL语句硬编码,跟业务代码混在一起
2. 参数只能按顺序传入,插入时候实体类和数据库字段顺序一致
3. 没有实现实体类到数据库插入对应记录的映射
4. 没有实现缓存等功能

3. 什么是ORM框架

为什么叫ORM? ORM框架解决了那些问题?
O: Object对象 R: relation关系型数据库 M: mapper 映射
Java和数据库记录的相互转化
ORM框架图
之前Hibernate也是一个比较优秀的ORM框架,使用hibernateTemplate会自动生成对应crud的语句;

Hibernate存在的缺点:
1. 不能指定部分字段,很不灵活
2. 无法自定义SQL,优化困难
3. 不支持动态SQL

4. Mybatis基本特性

基本特性:
1. 使用连接池对连接进行管理
2. SQL和业务代码分离解耦,集中进行管理
3. 支持参数映射和动态SQL
4. 结果集映射
5. 缓存管理
6. 重复SQL的提取
7. 插件机制,例如分页插件等

应该如何选择ORM框架?

  1. 业务简单 推荐Hibernate
  2. 业务复杂 需要灵活SQL 推荐mybatis
  3. 对性能要求高 推荐直接使用原生的JDBC
  4. Spring JDBC 和ORM混用
  5. 混用ORM基本没见过。。。推荐手写ORM!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值