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<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
<property name="driverClassName" value="$database.driverClassName"/>
<property name="url" value="$database.url"/>
<property name="username" value="$database.username"/>
<property name="password" value="$database.password"/>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:META-INF/mybatis/mybatis-config.xml" />
</bean>
部分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!