jdbctemplate,jpa,mybatis,mybatis-plus

Posted 激流勇进_38193118

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jdbctemplate,jpa,mybatis,mybatis-plus相关的知识,希望对你有一定的参考价值。

spring-boot-demo-orm-jdbctemplate

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <artifactId>spring-boot-demo-orm-jdbctemplate</artifactId>
   <version>1.0.0-SNAPSHOT</version>
   <packaging>jar</packaging>
   <parent>
      <groupId>com.xkcoding</groupId>
      <artifactId>spring-boot-demo</artifactId>
      <version>1.0.0-SNAPSHOT</version>
   </parent>
   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      <java.version>1.8</java.version>
   </properties>
   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-jdbc</artifactId>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
      </dependency>
      <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
      </dependency>
      <dependency>
         <groupId>cn.hutool</groupId>
         <artifactId>hutool-all</artifactId>
      </dependency>
      <dependency>
         <groupId>org.projectlombok</groupId>
         <artifactId>lombok</artifactId>
         <optional>true</optional>
      </dependency>
   </dependencies>
   <build>
      <finalName>spring-boot-demo-orm-jdbctemplate</finalName>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>
   </build>
</project>

BaseDao.java

@Slf4j
public class BaseDao<T, P> 
   private JdbcTemplate jdbcTemplate;
   private Class<T> clazz;
   @SuppressWarnings(value = "unchecked")
   public BaseDao(JdbcTemplate jdbcTemplate) 
      this.jdbcTemplate = jdbcTemplate;
      clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
   
   /**
    * 通用插入,自增列需要添加 @link Pk 注解
    *
    * @param t          对象
    * @param ignoreNull 是否忽略 null 值
    * @return 操作的行数
    */
   protected Integer insert(T t, Boolean ignoreNull) 
      String table = getTableName(t);
      List<Field> filterField = getField(t, ignoreNull);
      List<String> columnList = getColumns(filterField);
      String columns = StrUtil.join(Const.SEPARATOR_COMMA, columnList);
      // 构造占位符
      String params = StrUtil.repeatAndJoin("?", columnList.size(), Const.SEPARATOR_COMMA);
      // 构造值
      Object[] values = filterField.stream().map(field -> ReflectUtil.getFieldValue(t, field)).toArray();

      String sql = StrUtil.format("INSERT INTO table (columns) VALUES (params)", Dict.create().set("table", table).set("columns", columns).set("params", params));
      log.debug("【执行SQL】SQL:", sql);
      log.debug("【执行SQL】参数:", JSONUtil.toJsonStr(values));
      return jdbcTemplate.update(sql, values);
   

   /**
    * 通用根据主键删除
    *
    * @param pk 主键
    * @return 影响行数
    */
   protected Integer deleteById(P pk) 
      String tableName = getTableName();
      String sql = StrUtil.format("DELETE FROM table where id = ?", Dict.create().set("table", tableName));
      log.debug("【执行SQL】SQL:", sql);
      log.debug("【执行SQL】参数:", JSONUtil.toJsonStr(pk));
      return jdbcTemplate.update(sql, pk);
   

   /**
    * 通用根据主键更新,自增列需要添加 @link Pk 注解
    *
    * @param t          对象
    * @param pk         主键
    * @param ignoreNull 是否忽略 null 值
    * @return 操作的行数
    */
   protected Integer updateById(T t, P pk, Boolean ignoreNull) 
      String tableName = getTableName(t);
      List<Field> filterField = getField(t, ignoreNull);
      List<String> columnList = getColumns(filterField);
      List<String> columns = columnList.stream().map(s -> StrUtil.appendIfMissing(s, " = ?")).collect(Collectors.toList());
      String params = StrUtil.join(Const.SEPARATOR_COMMA, columns);

      // 构造值
      List<Object> valueList = filterField.stream().map(field -> ReflectUtil.getFieldValue(t, field)).collect(Collectors.toList());
      valueList.add(pk);

      Object[] values = ArrayUtil.toArray(valueList, Object.class);

      String sql = StrUtil.format("UPDATE table SET params where id = ?", Dict.create().set("table", tableName).set("params", params));
      log.debug("【执行SQL】SQL:", sql);
      log.debug("【执行SQL】参数:", JSONUtil.toJsonStr(values));
      return jdbcTemplate.update(sql, values);
   

   /**
    * 通用根据主键查询单条记录
    *
    * @param pk 主键
    * @return 单条记录
    */
   public T findOneById(P pk) 
      String tableName = getTableName();
      String sql = StrUtil.format("SELECT * FROM table where id = ?", Dict.create().set("table", tableName));
      RowMapper<T> rowMapper = new BeanPropertyRowMapper<>(clazz);
      log.debug("【执行SQL】SQL:", sql);
      log.debug("【执行SQL】参数:", JSONUtil.toJsonStr(pk));
      return jdbcTemplate.queryForObject(sql, new Object[]pk, rowMapper);
   

   /**
    * 根据对象查询
    *
    * @param t 查询条件
    * @return 对象列表
    */
   public List<T> findByExample(T t) 
      String tableName = getTableName(t);
      List<Field> filterField = getField(t, true);
      List<String> columnList = getColumns(filterField);
      List<String> columns = columnList.stream().map(s -> " and " + s + " = ? ").collect(Collectors.toList());

      String where = StrUtil.join(" ", columns);
      // 构造值
      Object[] values = filterField.stream().map(field -> ReflectUtil.getFieldValue(t, field)).toArray();

      String sql = StrUtil.format("SELECT * FROM table where 1=1 where", Dict.create().set("table", tableName).set("where", StrUtil.isBlank(where) ? "" : where));
      log.debug("【执行SQL】SQL:", sql);
      log.debug("【执行SQL】参数:", JSONUtil.toJsonStr(values));
      List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, values);
      List<T> ret = CollUtil.newArrayList();
      maps.forEach(map -> ret.add(BeanUtil.fillBeanWithMap(map, ReflectUtil.newInstance(clazz), true, false)));
      return ret;
   

   /**
    * 获取表名
    *
    * @param t 对象
    * @return 表名
    */
   private String getTableName(T t) 
      Table tableAnnotation = t.getClass().getAnnotation(Table.class);
      if (ObjectUtil.isNotNull(tableAnnotation)) 
         return StrUtil.format("``", tableAnnotation.name());
       else 
         return StrUtil.format("``", t.getClass().getName().toLowerCase());
      
   

   /**
    * 获取表名
    *
    * @return 表名
    */
   private String getTableName() 
      Table tableAnnotation = clazz.getAnnotation(Table.class);
      if (ObjectUtil.isNotNull(tableAnnotation)) 
         return StrUtil.format("``", tableAnnotation.name());
       else 
         return StrUtil.format("``", clazz.getName().toLowerCase());
      
   

   /**
    * 获取列
    *
    * @param fieldList 字段列表
    * @return 列信息列表
    */
   private List<String> getColumns(List<Field> fieldList) 
      // 构造列
      List<String> columnList = CollUtil.newArrayList();
      for (Field field : fieldList) 
         Column columnAnnotation = field.getAnnotation(Column.class);
         String columnName;
         if (ObjectUtil.isNotNull(columnAnnotation)) 
            columnName = columnAnnotation.name();
          else 
            columnName = field.getName();
         
         columnList.add(StrUtil.format("``", columnName));
      
      return columnList;
   

   /**
    * 获取字段列表 @code 过滤数据库中不存在的字段,以及自增列
    *
    * @param t          对象
    * @param ignoreNull 是否忽略空值
    * @return 字段列表
    */
   private List<Field> getField(T t, Boolean ignoreNull) 
      // 获取所有字段,包含父类中的字段
      Field[] fields = ReflectUtil.getFields(t.getClass());

      // 过滤数据库中不存在的字段,以及自增列
      List<Field> filterField;
      Stream<Field> fieldStream = CollUtil.toList(fields).stream().filter(field -> ObjectUtil.isNull(field.getAnnotation(Ignore.class)) || ObjectUtil.isNull(field.getAnnotation(Pk.class)));

      // 是否过滤字段值为null的字段
      if (ignoreNull) 
         filterField = fieldStream.filter(field -> ObjectUtil.isNotNull(ReflectUtil.getFieldValue(t, field))).collect(Collectors.toList());
       else 
         filterField = fieldStream.collect(Collectors.toList());
      
      return filterField;
   


application.yml

server:
  port

部分mybatis配置

<settings>
    <setting name="cacheEnabled" value="false" />
    <setting name="useGeneratedKeys" value="false" />
    <setting name="defaultExecutorType" value="REUSE" />
    <setting name="lazyLoadingEnabled" value="false"/>
</settings>

部分persistence.xml

<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>

【问题讨论】:

这能回答你的问题吗? What transaction manager should I use for JBDC template When using JPA ? 【参考方案1】:

我在这里找到了解决方案:What transaction manager should I use for JBDC template When using JPA ?

我使用的是 JpaTransactionManager 而不是 DataSourceTransactionManager。 JavaDochttp://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/orm/jpa/JpaTransactionManager.html

此事务管理器还支持在事务中直接访问数据源(即使用相同数据源的纯 JDBC 代码)。这允许混合访问 JPA 的服务和使用普通 JDBC 的服务(不知道 JPA)!应用程序代码需要遵循与 DataSourceTransactionManager 相同的简单连接查找模式(即 DataSourceUtils.getConnection(javax.sql.DataSource) 或通过 TransactionAwareDataSourceProxy)。 请注意,这需要配置供应商特定的 JpaDialect。

在我将 jpaVendorAdapter 添加到我的 entityManagerFactory 配置后,一切正常,JdbcTemplate 查询和 MyBatis 都按预期在同一个事务中运行。根据 JavaDoc,我想 jpaDialect 应该足够了,但现在是凌晨 4 点,所以我现在不会尝试 :)

<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
    <property name="persistenceUnitName" value="persistenceUnit"/>
    <property name="dataSource" ref="dataSource"/>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="true" />
            <property name="generateDdl" value="true" />
            <property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
        </bean>
    </property>
</bean>

【讨论】:

是的,只需设置 jpa 方言即可emf.setJpaDialect(new HibernateJpaDialect());【参考方案2】:

我还没有混合使用 MyBatis,但正如 tewe 建议的那样,只需将 jpaDialect 添加到 transactionManager 中也可以完成这项工作。

<bean class="org.springframework.orm.jpa.JpaTransactionManager"
    id="transactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
    <property name="jpaDialect">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
    </property>
</bean>

【讨论】:

【参考方案3】:

尝试使用:

<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

直接在 JDBC 级别上运行。所有持久性抽象(JPA/iBatis 和JdbcTemplate)最终都使用 JDBC,因此您需要在最高通用级别上处理事务。

在您的情况下,您使用的是通过 javax.persistence.EntityTransaction 抽象处理事务的 JpaTransactionManager。显然 iBatis 不知道 JPA 事务,因此推测它在它之外工作。

您不需要任何类加载器/仪器魔法,应该可以工作。

【讨论】:

"请注意,这需要配置供应商特定的 JpaDialect。" tewe 解决了问题-

以上是关于jdbctemplate,jpa,mybatis,mybatis-plus的主要内容,如果未能解决你的问题,请参考以下文章

spring boot 集成 jpa+hibernate+jdbcTemplate

使用 Spring boot 嵌入式 tomcat 连接池,无需 jdbctemplate、hibernate 或 JPA

数据持久化框架为什么放弃HibernateJPAMybatis,最终选择JDBCTemplate!

如何使用多数据源,同时使用jpa和jdbctemplate

后端开发:数据持久化框架为什么放弃HibernateJPAMybatis,最终选择JDBCTemplate!

spring jdbctemplate和java web中jdbc的区别,它有啥好处