MyBatis、Spring JDBC 和 Spring Data JPA:选择哪种持久化框架?

当涉及到选择 Java ORM 框架时,MyBatis、Spring JDBC 和 Spring Data JPA 是最常用的三个框架。以下是每个框架的一些关键特点:

  1. MyBatis:它是一种半自动的 ORM 框架,通过 SQL 映射文件(XML 文件)将 Java 对象映射到关系型数据库中的表。它提供了强大的 SQL 映射功能和动态 SQL 语句生成,使开发人员可以更好地控制 SQL 语句的生成和执行。MyBatis 适合那些需要更高的 SQL 控制权和更多灵活性的项目。

  2. Spring JDBC:它是 Spring 框架的一部分,提供了访问关系型数据库的简单和直接的方式。它不需要任何 ORM 映射或配置文件,通过使用 JdbcTemplate 类,开发人员可以轻松地编写类型安全的 SQL 语句和查询结果映射。Spring JDBC 适合那些需要更直接的 JDBC 访问并且不需要高级 ORM 功能的项目。

  3. Spring Data JPA:它是 Spring Data 子项目的一部分,提供了 JPA 规范的实现。JPA 是 Java 持久化 API 的标准,可以让开发人员将 Java 对象映射到关系型数据库中的表,同时提供了高级 ORM 功能,如缓存和关系映射。Spring Data JPA 通过提供一些简单的接口和默认实现,极大地简化了 JPA 的使用,并提供了多种数据访问的方式。Spring Data JPA 适合那些需要高级 ORM 功能和开发效率的项目。

因此,选择 MyBatis、Spring JDBC 还是 Spring Data JPA 取决于项目的需求和开发团队的技术水平。需要根据项目的具体情况进行评估和选择。

一 、JdbcTemplate

Spring对数据库的操作在jdbc上面做了深层次的封装,使用spring的注入功能,可以把DataSource注册到JdbcTemplate之中。

我们只需要在使用jdbcTemplate类中使用@Autowired进行注入即可:

@Autowired
private JdbcTemplate jdbcTemplate;

1.JdbcTemplate主要提供方法:

1.1 execute方法

execute方法可以用于执行任何SQL语句,一般用于执行DDL语句;

 public String createTable(){
     String sql = "CREATE TABLE `user` (\n" +
         "  `id` int(11) NOT NULL AUTO_INCREMENT,\n" +
         "  `user_name` varchar(255) DEFAULT NULL,\n" +
         "  `pass_word` varchar(255) DEFAULT NULL,\n" +
         "  PRIMARY KEY (`id`)\n" +
         ") ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;\n" +
         "\n";
     jdbcTemplate.execute(sql);
     return "创建User表成功";
 }

1.2 update方法

update方法用于执行新增、修改、删除等语句;

 public void addUser(User user) {
     String sql = "insert into user (username, password) values (?, ?)";
     jdbcTemplate.update(sql, user.getUsername(), user.getPassword());
 }

如上,插入代码用的是update方法,其实增删改用的都是update方法,而查询则是和query相关的方法。

public void deleteUser( ) {
    String sql = "delete from user where username= ?";
    jdbcTemplate.update(sql, "小王");
}
​
public void updateUser(User user) {
    String sql = "update user set username=? where username= ?";
    jdbcTemplate.update(sql,  user.getUsername() + "_new", user.getUsername());
}
​
public void updateUser(User user) {
    String sql = "update user set username=? where username= ?";
    jdbcTemplate.update(sql,  user.getUsername() + "_new", user.getUsername());
}
​

1.3 batchUpdate方法

batchUpdate方法用于执行批处理相关语句;

public void batchUpdate() {
    String sql="insert into user (name,deptid) values (?,?)";
    List batchArgs=new ArrayList();
    batchArgs.add(new Object[]{"caoyc",6});
    batchArgs.add(new Object[]{"zhh",8});
    batchArgs.add(new Object[]{"cjx",8});
    jdbcTemplate.batchUpdate(sql, batchArgs);
}

使用jdbcTempalte的batchUpdate方法,第二个参数传入接口BatchPreparedStatementSetter接口,该接口需要实现两个方法,getBatchSize()用于获得该批数量,setValues(PreapredStatement ps, int i)用于设置每个PreparedStatement

 public Integer batchSaveWspc(List wspcList) {
        String sql
            = "insert into db_sjzx.t_data_wspc(c_bh,c_ah,c_corp,c_bpcr,n_pcdf,dt_pcrq,dt_djsj) values(?,?,?,?,?,?,?)";
        jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setString(1, UUIDHelper.getUuid());
                ps.setString(2, wspcList.get(i).getAh());
                ps.setString(3, wspcList.get(i).getCorp());
                ps.setString(4, wspcList.get(i).getBpcr());
                ps.setBigDecimal(5, wspcList.get(i).getPcdf());
                Date pcrq = wspcList.get(i).getPcrq();
                java.sql.Date pcrqVO = new java.sql.Date(pcrq.getTime());
                ps.setDate(6, pcrqVO);
                Date djsj = new Date();
                java.sql.Date djsjVO = new java.sql.Date(djsj.getTime());
                ps.setDate(7, djsjVO);
            }
        @Override
        public int getBatchSize() {
            return wspcList.size();
        }
    });
    return wspcList.size();
}

1.4 queryForXXX方法

query方法及queryForXXX方法:用于执行查询相关语句;

a. 查询表的记录数

 @Test
 public void test1() {
     String sql = "select count(*) from user";
     Long row = jdbcTemplate.queryForObject(sql, Long.class);
     System.out.println("查询出来的记录数为:" + row);
 }

b. 查询返回对象

   @Test
   public void test2() {
       String sql = "select username, password from user where username = ?";
       Object[] object = {"mary_new"};
          User user = jdbcTemplate.queryForObject(sql, object,  new UserMapper());
      System.out.println(user);
  }

除此之外要实现结构RowMapper来新建一个映射类:

public class UserMapper implements RowMapper{
 
     @Override
     public User mapRow(ResultSet resultSet, int rows) throws SQLException {
         User user = new User();
         user.setUsername(resultSet.getString(1));
         user.setPassword(resultSet.getString(2));
         return user;
     }
 }

要注意这个UserMapper.java应该要和具体的Sql语句对应。

c. 查询并返回List集合

@Test
public void test3() {
    String sql = "select * from user";
    List users = jdbcTemplate.query(sql, new UserMapper());
    for(User u: users) {
        System.out.println(u);
    }
}

c. 查询并返回Map集合

public CollectResult collect(CollectParam param) {
        Object[] objects = new Object[] {param.getKpdx(), param.getKpjh().getKsrq(), param.getKpjh().getJsrq()};
        Map countMap
            = jdbcTemplate.queryForMap(CorpTableRouter.genSql(tsgkSpzJasSql.toString(), param.getKpjh().getCorp()), objects);
        List> dataList
            = jdbcTemplate.queryForList(CorpTableRouter.genSql(tsgkSpzSql.toString(), param.getKpjh().getCorp()), objects);
        return tsgkCollectorDate.tsgkDateDetail(countMap, dataList, collectorUtil.getJczbName(param.getJszb().getValue()));
    }

··

对于spring的JdbcTemplate进行结果集查询操作,spring给我们开发的是一系列的query方法,这些查询的方法中回调的接口主有三种:ResultSetExtractor,RowCallbackHandler,RowMapper,这个内容有图有真相:

MyBatis、Spring JDBC 和 Spring Data JPA:选择哪种持久化框架?_第1张图片

使用spring的JdbcTemplate进行查询的三种回调方式的比较 - - ITeye博客

二、Mybatis

1.Mybatis介绍

MyBatis 是一个基于Java的持久层框架。它提供的持久层框架包括SQL Maps和Data Access Objects(DAO)。

MyBatis是一个支持普通SQL查询存储过程高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的XML或注解用于配置和原始映射,将接口和 Java 的POJO(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录

每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。SqlSessionFactoryBuilder从一个xml配置文件或者一个预定义的配置类的实例获得配置信息。

Mybatis核心类:

 SqlSessionFactory:每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或通过Java的方式构建出 SqlSessionFactory 的实例。SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,建议使用单例模式或者静态单例模式。一个SqlSessionFactory对应配置文件中的一个环境(environment),如果你要使用多个数据库就配置多个环境分别对应一个SqlSessionFactory。

SqlSession:SqlSession是一个接口,它有2个实现类,分别是DefaultSqlSession(默认使用)以及SqlSessionManager。SqlSession通过内部存放的执行器(Executor)来对数据进行CRUD。此外SqlSession不是线程安全的,因为每一次操作完数据库后都要调用close对其进行关闭,官方建议通过try-finally来保证总是关闭SqlSession。

Executor:Executor(执行器)接口有两个实现类,其中BaseExecutor有三个继承类分别是BatchExecutor(重用语句并执行批量更新),ReuseExecutor(重用预处理语句prepared statements),SimpleExecutor(普通的执行器)。以上三个就是主要的Executor。通过下图可以看到Mybatis在Executor的设计上面使用了装饰者模式,我们可以用CachingExecutor来装饰前面的三个执行器目的就是用来实现缓存。

MappedStatement:MappedStatement就是用来存放我们SQL映射文件中的信息包括sql语句,输入参数,输出参数等等。一个SQL节点对应一个MappedStatement对象。

 原理图如下:

MyBatis、Spring JDBC 和 Spring Data JPA:选择哪种持久化框架?_第2张图片

2.mybatis配置文件

configuration.xml

创建mybatis的配置文件,配置数据库的信息.

可以配置多个运行环境,但是每个SqlSessionFactory 实例只能选择一个运行环境





	
	
	
	
	
	
		
		
			
			
			
			
				
				
				
				
				
			
		
		
		
		
		
			
			
			
			
				
				
				
				
				
			
		
	
    
	
        
    
	

properties(属性)

  • 将数据库连接的参数单独配置在,db.properties中,只需要在SqlMapConfig.xml中加载db.properties的属性值。在SqlMapConfig.xml中就不需要对数据库连接参数硬编码。

settings(全局配置参数)

  • mybatis框架在运行时可以调整一些运行参数。比如:开启二级缓存、开启延迟加载。全局参数将会影响mybatis的运行行为。

    img

typeAliases(类型别名)

  • 单个定义 这里写图片描述

  • 批量定义 这里写图片描述

  • mybatis默认支持的别名

    img

  • environments(环境集合属性对象)

    • environment(环境子属性对象)

      • transactionManager(事务管理)

      • dataSource(数据源)

  • mappers(映射器)

    • 通过resource 这里写图片描述

    • 通过class 这里写图片描述

    • 通过package(推荐使用) 这里写图片描述

XXXXMapper.xml




  
     
    
      
          SELECT LAST_INSERT_ID()
      
      insert into user (username,birthday,sex,address) value(#{username},#{birthday},#{sex},#{address})
    
    
    
        delete from user where id=#{id}
    

    
        UPDATE user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
    
    
        
    
    

这个是个简单的插入过程,我们的parameterType是个全路径com.zhao.entity.User,如果每个都写这么全的话 会很麻烦,所以我们可以配置它的别名,当然,别名要配置在configuration.xml中

    
        
    

3.mapper开发

mapper开发只需要遵守几个规范即可

  • 在mapper.xml中namespace等于mapper接口地址

  • mapper.java接口中的方法名和mapper.xml中statement的id一致

  • mapper.java接口中的方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致。

  • mapper.java接口中的方法返回值类型和mapper.xml中statement的resultType指定的类型一致。

mapper开发xml中的标签介绍

  • parameterType

    sql语句中需要传入的参数, 类型要与对应的接口方法的类型一致

  • resultType

    使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。

  • resultMap

    mybatis中使用resultMap完成高级输出结果映射。

     
         
         
    `

使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。

  • 动态sql:if choose trim foreach

    MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

    通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语言来改进这种情形,这种语言可以被用在任意的 SQL 映射语句中。

    动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多的元素需要来了解。MyBatis 3 大大提升了它们,现在用不到原先一半的元素就可以了。MyBatis 采用功能强大的基于 OGNL 的表达式来消除其他元素。

    • if : test表达式、OGNL:特殊符号写转移字符 where

    • choose (when, otherwise) : choose:标签中,when、otherwise

      • trim (where, set) :trim:整个,prefix、prefixOverrides1 2前缀覆盖、suffix、suffixOverrides

    • foreach:foreach:collection、item、separator、open、close、index (遍历list时index是索引,item是当前值;遍历map时index是map的key,item是map的值)



  
     delete from product where product_Id in  
       
         #{productId,jdbcType = VARCHAR}  
       
   

高级结果集映射

eg:查询订单信息,关联查询创建订单的用户信息

实现思路:使用resultMap将查询结果中的订单信息映射到Orders对象中,在orders类中添加User属性,将关联查询出来的用户信息映射到orders对象中的user属性中。 第一步:Orders类中添加User 的user属性。上面的代码已经添加 第二步:mapper.xml中定义ResultMap及其查询 第三步:接口中定义相应的方法



        
        
        
        
        
    
        
            
            
            
            
            
            
        

 

    

  • resultMap: 使用association和collection完成一对一和一对多高级映射(对结果有特殊的映射要求)。

    • association: 作用:将关联查询信息映射到一个pojo对象中。 场合:为了方便查询关联信息可以使用association将关联订单信息映射为用户对象的pojo属性中。

    • collection: 作用:将关联查询信息映射到一个list集合中。

      场合:为了方便查询遍历关联信息可以使用collection将关联信息映射到list集合中,

Mybatis延迟加载

resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。

  1. Mybatis的延迟加载功能默认是关闭的

  2. 需要在SqlMapConfig.xml文件中通过setting标签配置来开启延迟加载功能

  3. 开启延迟加载的属性: lazyLoadingEnabled:全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。默认为false aggressiveLazyLoading:当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。默认为true

  4. 配置

     
         
         
         
         
     

4.Mybatis查询缓存

mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。如果缓存中有数据就不用从数据库中获取,大大提高系统性能。mybaits提供一级缓存,和二级缓存。

  • 一级缓存

    一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

    如果是执行两个service调用查询相同 的用户信息,不走一级缓存,因为session方法结束,sqlSession就关闭,一级缓存就清空。

  • 二级缓存

    二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

5. 注解开发

MyBatis的注解,主要是用于替换映射文件。而映射文件中无非存放着增删改查的sql映射标签。所以,MyBatis注解,就是替换映射文件中的sql标签。

常用CRUD注解开发

@Insert : 插入sql , 和xml insert sql语法完全一样

@SelectKey:用于替换xml中的标签,用于返回新插入数据的id值。

@Select : 查询sql, 和xml select sql语法完全一样

  • statement:获取新插入记录主键值得sql语句

  • keyProperty:获取的该主键值返回后初始化对象的那个属性

  • resultType:返回值类型

  • before:指定主键的生成相对于insert语句的执行先后顺序,该属性不能省略

@Update : 更新sql, 和xml update sql语法完全一样

@Delete : 删除sql, 和xml delete sql语法完全一样

@Param : 入参

@Results : 结果集合

@Result : 结果

public interface IStudentDao {
    @Insert(value={"insert into student(name,age,score) values(#{name},#{age},#{score})"})
    void insertStudent(Student student);    
    
    @Insert("insert into student(name,age,score) values(#{name},#{age},#{score})")
    @SelectKey(statement="select @@identity",resultType=int.class,keyProperty="id",before=false)
    void insertStudentCacheId(Student student);
    
    @Delete(value="delete from student where id=#{id}")
    void deleteStudentById(int id);
    
    @Update("update student set name=#{name},age=#{age},score=#{score} where id=#{id}")
    void updateStudent(Student student);
    
    @Select("select * from student")
    List selectAllStudents();
    
    @Select("select * from student where id=#{id}")
    Student selectStudentById(int id);
    
    @Select("select * from student where name like '%' #{name} '%'")
    List selectStudentsByName(String name);
    
}

动态sql

  • 脚本sql

    XML配置方式的动态SQL在上面的xml方式开发中有体现,下面是用") public List findUserById(User user);

    很明显,在java中写xml可读性和维护性太差,尤其当SQL很长时,这样写是很痛苦的。

  • 在方法中构建sql

    接口中是不能写实现的,所以这里借用内部类来生成动态SQL。增改删也有对应的@InsertProvider、@UpdateProvider、@DeleteProvider,下面是@SelectProvider的使用实例:

 /**
     * 
     * AjMapper
     * 
     * @description 查询案件信息
     * @param column 法标
     * @param table 表名
     * @return List>
     * @author pujihong
     * @date 2019年3月18日 下午5:11:54
     */
    @SelectProvider(type = AjProvider.class, method = "queryAjList")
    List getAjList(@Param("column") String column, @Param("table") String table);
/**
     * 
     * AjProvider
     * 
     * @description 指标案件查询
     * @param map 查询信息
     * @return String
     * @author pujihong
     * @date 2019年3月22日 上午10:32:44
     */
    public String queryAjList(Map map) {
        StringBuilder sqlSelect = new StringBuilder();
        String table = (String)map.get("table");
        String column = (String)map.get("column");
        sqlSelect
            .append(
                "select n_ajbs as ajbh,c_ah as ah,c_cbr as cbr,c_cbspt as ts,to_char(d_larq, 'YYYY-MM-DD') as larq,")
            .append("to_char(d_jarq, 'YYYY-MM-DD') as jarq,n_sfxg as sfxg,")
            .append(CorpTableRouter.genSql("#{corp} AS zb ", column))
            .append(CorpTableRouter.genSql("from db_sjzx.#{corp}", table));
        return sqlSelect.toString();
    }

这比

你可能感兴趣的:(mybatis,spring,java,sql,后端)