Spring JPA与Hibernate学习使用

EntityManager

在使用持久化工具的时候,一般都有一个对象来操作数据库,在原生的Hibernate中叫做Session,在 JPA 中叫做EntityManager,在MyBatis中叫做SqlSession,通过这个对象来操作数据库。

EntityManager是 JPA 中用于增删改查的接口,连接内存中的 java 对象和数据库的数据存储。Hibernate EntityManager是围绕提供JPA编程接口实现的Hibernate Core的一个包装,支持JPA实体实例的生命周期,并允许用标准的Java Persistence查询语言编写查询。

EntityManager称为实体管理器,它由EntityManagerFactory所创建。EntityManagerFactory,作为EntityManager的工厂,包含有当前O-R映射的元数据信息,每个EntityManagerFactory,可称为一个持久化单元(PersistenceUnit),每个持久化单元可认为是一个数据源的映射(所谓数据源,可理解为一个数据库,可以在应用服务器中配置多个数据源,同时使用不同的PersistenceUnit来映射这些数据源,从而能够很方便的实现跨越多个数据库之间的事务操作!)

PersistenceContext,称为持久化上下文,它一般包含有当前事务范围内的,被管理的实体对象(Entity)的数据。每个EntityManager,都会跟一个PersistenceContext相关联。PersistenceContext中存储的是实体对象的数据,而关系数据库中存储的是记录,EntityManager正是维护这种OR映射的中间者,它可以把数据从数据库中加载到PersistenceContext中,也可以把数据从PersistenceContext中持久化到数据库,EntityManager通过Persist、merge、remove、refresh、flush等操作来操纵PersistenceContext与数据库数据之间的同步!

EntityManager是应用程序操纵持久化数据的接口。它的作用与hibernate session类似。为了能够在一个请求周期中使用同一个session对象,在hibernate的解决方案中,提出了currentSession的概念,hibernate中的current session,可以跟JTA事务绑定,也可以跟当前线程绑定。在hibernate中,session管理着所有的持久化对象的数据。而在EJB3中,EntityManager管理着PersistenceContext,PersistenceContext正是被管理的持久化对象的集合。
以下是基于项目中使用来说的

依赖

xml 复制代码
<spring-boot.version>2.5.6</spring-boot.version>
<hibernate-types-52.version>2.14.0</hibernate-types-52.version>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- hibernate支持配置 -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-ehcache</artifactId>
</dependency>
<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>${hibernate-types-52.version}</version>
</dependency>
<!-- hibernate支持配置 -->

配置

properties 复制代码
spring.datasource.url=jdbc:postgresql://localhost:5432/demo
spring.datasource.username=demo
spring.datasource.password=demo
spring.datasource.driver-class-name=org.postgresql.Driver
#该选项用来验证数据库连接的有效性。如果您的驱动程序支持JDBC4,我们强烈建议您不要设置此属性
spring.datasource.hikari.connection-test-query=SELECT 1
#池中最大连接数,包括闲置和使用中的连接
spring.datasource.hikari.maximum-pool-size=100
#自动提交从池中返回的连接
spring.datasource.hikari.auto-commit=true
#池中维护的最小空闲连接数
spring.datasource.hikari.minimum-idle=10
#连接允许在池中闲置的最长时间
spring.datasource.hikari.idle-timeout=30000
#连接池的用户定义名称,主要出现在日志记录和JMX管理控制台中以识别池和池配置
spring.datasource.hikari.pool-name=DatebookHikariCP
#池中连接最长生命周期
spring.datasource.hikari.max-lifetime=1800000
#等待来自池的连接的最大毫秒数
spring.datasource.hikari.connection-timeout=30000

spring.jpa.database=POSTGRESQL
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
#spring.jpa.properties.hibernate.current_session_context_class=thread
spring.jpa.properties.hibernate.hbm2ddl.auto=update//自动创建表
#spring.jpa.properties.hibernate.default_schema=mss
spring.jpa.properties.hibernate.dialect=com.xysd.common.utils.AdvancedPostgreSQLDialect
spring.jpa.properties.hibernate.show_sql=false
spring.jpa.properties.hibernate.format_sql=false
spring.jpa.properties.hibernate.use_sql_comments=false
spring.jpa.properties.hibernate.jdbc.batch_size=500
spring.jpa.properties.hibernate.default_batch_fetch_size=160
spring.jpa.properties.hibernate.max_fetch_depth=3
spring.jpa.properties.hibernate.batch_fetch_style=DYNAMIC
spring.jpa.properties.hibernate.jdbc.fetch_size=500
spring.jpa.properties.hibernate.cache.use_second_level_cache=false
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
spring.jpa.properties.hibernate.cache.use_query_cache=false
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.query.substitutions=true 1,false 0
#避免KingBase报createClob 错误
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true

SpringSessionContext
AdvancedPostgreSQLDialect

java 复制代码
public class AdvancedPostgreSQLDialect extends PostgreSQL10Dialect {
    
    public AdvancedPostgreSQLDialect(){
        super();
        
        this.registerColumnType(Types.CLOB, "text");
        
        this.registerColumnType(Types.BOOLEAN, "bit");

        this.registerHibernateType(Types.ARRAY, StandardBasicTypes.CHARACTER_ARRAY.getName());
        //支持jsonb extAttr属性
        this.registerColumnType(Types.OTHER, "jsonb" );
    }
    
}

支持JPA :LocalContainerEntityManagerFactoryBean--->得到EntityManager--->得到session
不支持JPA :LocalSessionFactoryBean--->得到session
LocalContainerEntityManagerFactoryBean与LocalSessionFactoryBean

封装JPA操作

java 复制代码
@SuppressWarnings("rawtypes")
public class JpaQueryBuilder{
    static boolean isMonitorPerformance() {
      return false;
    }
    /**
     * 创建query
     * 通过params设置query查询参数
     * @param hql
     * @param params
     */
    static Query createHQLQueryByParams(Session session, String hql, Object... params){
        Query query = session.createQuery(hql);
        setParamsWithQuery(query, params);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(Query.class, hql,query);
        }
        return query;
    }
    /**
     * 创建query
     * 通过params设置query查询参数
     * @param <T>
     * @param hql
     * @param params
     */
    static <T> Query<T> createHQLQueryByParams(Session session, Class<T> resultType, String hql,
            Object... params){
        Query<T> query = session.createQuery(hql,resultType);
        setParamsWithQuery(query, params);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(Query.class, hql,query);
        }
        return query;
    }
    /**
     * 创建query
     * 通过params设置query查询参数
     * @param hql
     * @param params
     */
    static Query createHQLQueryByMapParams(Session session, String hql, Map<String,Object> params){
        Query query = session.createQuery(hql);
        setNamedParamsWithQuery(query, params);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(Query.class, hql,query);
        }
        return query;
    }
    /**
     * 创建query
     * 通过params设置query查询参数
     * @param <T>
     * @param hql
     * @param params
     */
    static <T> Query<T> createHQLQueryByMapParams(Session session, Class<T> resultType, String hql,
            Map<String,Object> params){
        Query<T> query = session.createQuery(hql,resultType);
        setNamedParamsWithQuery(query, params);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(Query.class, hql,query);
        }
        return query;
    }
    /**
     * 创建query 带分页
     * 通过params设置query查询参数
     * @param hql
     * @param params
     */
    static Query createPageHQLQueryByMapParams(Session session, String hql, int pageNo, int pageSize,
            Map<String,Object> params){
        Query query = session.createQuery(hql);
        setNamedParamsWithQuery(query, params);
        int startIndex=(pageNo-1)*pageSize;
        query.setFirstResult(startIndex).setMaxResults(pageSize);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(Query.class, hql,query);
        }
        return query;
    }
    /**
     * 创建query 带分页
     * 通过params设置query查询参数
     * @param <T>
     * @param hql
     * @param params
     */
    static <T> Query<T> createPageHQLQueryByMapParams(Session session, Class<T> resultType, String hql,
            int pageNo, int pageSize, Map<String,Object> params){
        Query<T> query = session.createQuery(hql,resultType);
        setNamedParamsWithQuery(query, params);
        int startIndex=(pageNo-1)*pageSize;
        query.setFirstResult(startIndex).setMaxResults(pageSize);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(Query.class, hql,query);
        }
        return query;
    }
    
    
    /**
     * 创建NativeQuery
     * @param sql
     * @param params 参数为数组
     */
    static NativeQuery createSQLQueryByParams(Session session, String sql, Object... params){
        NativeQuery query = session.createNativeQuery(sql);
        setParamsWithQuery(query, params);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(NativeQuery.class, sql,query);
        }
        return query;
    }
    /**
     * 创建NativeQuery
     * @param <T>
     * @param sql
     * @param params 参数为数组
     */
    static <T> NativeQuery<T> createSQLQueryByParams(Session session, Class<T> resultType, String sql,
            Object... params){
        NativeQuery<T> query = session.createNativeQuery(sql,resultType);
        setParamsWithQuery(query, params);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(NativeQuery.class, sql,query);
        }
        return query;
    }
    /**
     * 创建NativeQuery
     * @param sql
     * @param params
     */
    static NativeQuery createSQLQueryByMapParams(Session session, String sql, Map<String,Object> params){
        NativeQuery query = session.createNativeQuery(sql);
        setNamedParamsWithQuery(query, params);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(NativeQuery.class, sql,query);
        }
        return query;
    }
    /**
     * 创建NativeQuery
     * @param <T>
     * @param sql
     * @param params
     */
    static <T> NativeQuery<T> createSQLQueryByMapParams(Session session, Class<T> resultType, String sql,
            Map<String,Object> params){
        NativeQuery<T> query = session.createNativeQuery(sql,resultType);
        setNamedParamsWithQuery(query, params);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(NativeQuery.class, sql,query);
        }
        return query;
    }
    /**
     * 创建NativeQuery 带分页
     * @param pageNo
     * @param params
     */
    static NativeQuery createPageSQLQueryByMapParams(Session session, String sql, int pageNo, int pageSize,
            Map<String,Object> params){
        NativeQuery query = session.createNativeQuery(sql);
        setNamedParamsWithQuery(query, params);
        int startIndex=(pageNo-1)*pageSize;
        query.setFirstResult(startIndex).setMaxResults(pageSize);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(NativeQuery.class, sql,query);
        }
        return query;
    }
    /**
     * 创建NativeQuery 带分页
     * @param <T>
     * @param sql
     * @param params
     */
    static <T> NativeQuery<T> createPageSQLQueryByMapParams(Session session, Class<T> resultType, String sql,
            int pageNo, int pageSize, Map<String,Object> params
            ){
        NativeQuery<T> query = session.createNativeQuery(sql,resultType);
        setNamedParamsWithQuery(query, params);
        int startIndex=(pageNo-1)*pageSize;
        query.setFirstResult(startIndex).setMaxResults(pageSize);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(NativeQuery.class, sql,query);
        }
        return query;
    }
    
    //设置参数
    private static void setParamsWithQuery(Query query, Object... params) {
        if (params==null||params.length==0) {
            return;
        }
        for(int i=0;i<params.length;i++){
            query.setParameter(i,params[i]);
        }
    }
    //设置参数
    private static void setNamedParamsWithQuery(Query query, Map<String, Object> params) {
        if (params==null||params.size()==0) {
            return;
        }
        for(Map.Entry<String,Object> entry:params.entrySet()){
            Object paramKey = entry.getKey();
            Object paramValue = entry.getValue();
            if(paramValue instanceof List){
                query.setParameterList((String)paramKey, (List)paramValue);
            }else if(paramValue instanceof String[]){
                query.setParameterList((String)paramKey, (String[])paramValue);
            }else if(paramValue instanceof Object[]){
                query.setParameterList((String)paramKey, (Object[])paramValue);
            }else if(paramValue instanceof Collection){
                query.setParameterList((String)paramKey, (Collection)paramValue);
            }else{
                query.setParameter((String)paramKey, paramValue);
            }
        }
    }
    
}

对外提供的服务

java 复制代码
@SuppressWarnings({"rawtypes","unchecked"})
public abstract class JpaHibernateRepository{
    protected Logger logger = LogManager.getLogger(this.getClass());
    
    @Autowired
    @PersistenceContext
    private EntityManager em;
    
    protected EntityManager getEm(){
        return em;
    }
    
    /**
     * 根据jpa EntityManager 获取hibernate session
     * @return
     */
    protected Session getSession(){
        return (Session) getEm().getDelegate();
    }

	private boolean isMonitorPerformance() {
        return true;
    }
    
    /**
     * 创建query
     * 通过params设置query查询参数
     * @param hql
     * @param params
     */
    protected Query createHQLQueryByParams(String hql,Object... params){
        Query query = JpaQueryBuilder.createHQLQueryByParams(getSession(), hql, params);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(Query.class, hql,query);
        }
        return query;
    }
    /**
     * 创建query
     * 通过params设置query查询参数
     * @param <T>
     * @param hql
     * @param params
     */
    protected <T> Query<T> createHQLQueryByParams(Class<T> resultType,String hql,Object... params){
        Query<T> query = JpaQueryBuilder.createHQLQueryByParams(getSession(), resultType, hql, params);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(Query.class,hql, query);
        }
        return query;
    }
    /**
     * 创建query
     * 通过params设置query查询参数
     * @param hql
     * @param params
     */
    protected Query createHQLQueryByMapParams(String hql,Map<String,Object> params){
        Query query = JpaQueryBuilder.createHQLQueryByMapParams(getSession(), hql, params);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(Query.class, hql,query);
        }
        return query;
    }  
    /**
     * 创建query
     * 通过params设置query查询参数
     * @param <T>
     * @param hql
     * @param params
     */
    protected <T> Query<T> createHQLQueryByMapParams(Class<T> resultType,String hql,Map<String,Object> params){
        Query<T> query = JpaQueryBuilder.createHQLQueryByMapParams(getSession(), resultType, hql, params);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(Query.class, hql,query);
        }
        return query;
    }
    /**
     * 创建query 带分页
     * 通过params设置query查询参数
     * @param hql
     * @param params
     */
    protected Query createPageHQLQueryByMapParams(String hql,int pageNo,int pageSize,
            Map<String,Object> params){
        Query query = JpaQueryBuilder.createPageHQLQueryByMapParams(getSession(), hql, pageNo, pageSize, params);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(Query.class, hql,query);
        }
        return query;
    }
    /**
     * 创建query 带分页
     * 通过params设置query查询参数
     * @param <T>
     * @param hql
     * @param params
     */
    protected <T> Query<T> createPageHQLQueryByMapParams(Class<T> resultType,String hql,int pageNo,int pageSize,
            Map<String,Object> params){
        Query<T> query = JpaQueryBuilder.createPageHQLQueryByMapParams(getSession(), resultType, hql, pageNo, pageSize, params);
        if(isMonitorPerformance()) {
            return QueryProxy.newProxyQuery(Query.class, hql,query);
        }
        return query;
    }
     //批量执行sql更新
    protected int batchUpdateSql(final String sql,final List<Object[]> batchParams){
        long start = System.currentTimeMillis();
        final BatchUpdatedResult updateResult = new BatchUpdatedResult();
        this.getSession().doWork(new Work() {
            @Override
            public void execute(Connection conn) throws SQLException {
                PreparedStatement ps = null ;
                try{
                    ps = conn.prepareStatement(sql);
                    for(Object[] params : batchParams){
                        if(params == null){
                            continue;
                        }
                        for(int i=0;i<params.length;i++){
                            Object p = params[i];
                            if(p == null){
                                ps.setNull(i+1,Types.VARCHAR);
                            }
                            else if( p instanceof String){
                                String pstr = (String)p;
                                ps.setString(i+1, pstr);
                            }else if(p instanceof Integer){
                                int n = (Integer)p;
                                ps.setInt(i+1, n);
                            }else if(p instanceof Date){
                                Date d = (Date)p;
                                ps.setTimestamp(i+1, new Timestamp(d.getTime()));
                            }else if(p instanceof Long){
                                Long d = (Long)p;
                                ps.setLong(i+1, d);
                            }else if(p instanceof Byte){
                                Byte d = (Byte)p;
                                ps.setByte(i+1, d);
                            }else if(p instanceof Boolean){
                                Boolean d = (Boolean)p;
                                ps.setBoolean(i+1, d);
                            }else if(p instanceof Double){
                                Double d = (Double)p;
                                ps.setDouble(i+1, d);
                            }else if(p instanceof BigDecimal){
                                BigDecimal d = (BigDecimal)p;
                                ps.setBigDecimal(i+1, d);
                            }else if(p instanceof Float){
                                Float d = (Float)p;
                                ps.setFloat(i+1, d);
                            }
                            else{
                                throw new RuntimeException("未识别的参数类型:"+p.getClass());
                            }
                        }
                        
                        ps.addBatch();
                    }
                    int[] updateds = ps.executeBatch();
                    for(int update:updateds) {
                        updateResult.addUpdated(update);
                    }
                }finally{
                    if(ps!=null){
                        ps.close();
                    }
                    
                }
            }
        });
        logger.info("耗时["+(System.currentTimeMillis()-start)+"]毫秒批量执行SQL:"+sql+"");
        return updateResult.updated;
    }
    //批量执行sql更新
    protected int batchUpdateSql(final String sql,final List<Object[]> batchParams,int[] sqlTypes){
        long start = System.currentTimeMillis();

        final BatchUpdatedResult updateResult = new BatchUpdatedResult();
        this.getSession().doWork(new Work() {
            @Override
            public void execute(Connection conn) throws SQLException {
                PreparedStatement ps = null ;
                try{
                    ps = conn.prepareStatement(sql);
                    for(Object[] params : batchParams){
                        if(params == null){
                            continue;
                        }
                        if(sqlTypes==null || sqlTypes.length!=params.length){
                            throw new IllegalArgumentException("sqlTypes.length must be equal to params.length");
                        }
                        for(int i=0;i<params.length;i++){
                            Object p = params[i];
                            
                            if(p == null){
                                ps.setNull(i+1,sqlTypes[i]);
                            }
                            else if( p instanceof String){
                                String pstr = (String)p;
                                ps.setString(i+1, pstr);
                            }else if(p instanceof Integer){
                                int n = (Integer)p;
                                ps.setInt(i+1, n);
                            }else if(p instanceof Date){
                                Date d = (Date)p;
                                ps.setTimestamp(i+1, new Timestamp(d.getTime()));
                            }else if(p instanceof Long){
                                Long d = (Long)p;
                                ps.setLong(i+1, d);
                            }else if(p instanceof Byte){
                                Byte d = (Byte)p;
                                ps.setByte(i+1, d);
                            }else if(p instanceof Boolean){
                                Boolean d = (Boolean)p;
                                ps.setBoolean(i+1, d);
                            }else if(p instanceof Double){
                                Double d = (Double)p;
                                ps.setDouble(i+1, d);
                            }else if(p instanceof BigDecimal){
                                BigDecimal d = (BigDecimal)p;
                                ps.setBigDecimal(i+1, d);
                            }else if(p instanceof Float){
                                Float d = (Float)p;
                                ps.setFloat(i+1, d);
                            }
                            else{
                                throw new RuntimeException("未识别的参数类型:"+p.getClass());
                            }
                        }
                        
                        ps.addBatch();
                    }
                    int[] updateds = ps.executeBatch();
                    for(int update:updateds) {
                        updateResult.addUpdated(update);
                    }
                }finally{
                    if(ps!=null){
                        ps.close();
                    }
                }
            }
        });
        logger.info("耗时["+(System.currentTimeMillis()-start)+"]毫秒批量执行SQL:"+sql+"");
        return updateResult.updated;
    }

	 /**
     * 安全获取实体
     * @param <T>
     * @param entityClass
     * @param id
     * @return
     */
    public <T> T safeGet(Class<T> entityClass, Serializable id) {
        if(id==null) return null;
        if((id instanceof String)&&StringUtils.isBlank((String)id)) {
            return null;
        }
        return this.getSession().get(entityClass, id);
    }
    /**
     * 根据Ids查找entity列表
     * @param entityClass
     * @param orderedIds
     * @return
     */
    public <T> List<T> findByOrderedIds(Class<T> entityClass,Collection<String> orderedIds,String... loadFetchs){
        List<T> result = new ArrayList<T>();
        if(orderedIds == null || orderedIds.size() == 0 ){
            return result;
        }
        List<List<String>> splittedIdsList = Utils.splitToFixedList(orderedIds, 800);
        Map<String, T> entityMap = new HashMap<String, T>();
        String hql="select o from "+entityClass.getName()+" o ";
        if(loadFetchs!=null) {
            for (int i=0;i<loadFetchs.length;i++) {
                hql=hql+"left join fetch o."+loadFetchs[i]+" o"+i+" ";
            }
        }
        hql=hql+"where o.id in (:ids)";
        for(List<String> ids : splittedIdsList){
            List<T> entities = this.createHQLQueryByMapParams(entityClass, hql,
                    Utils.buildMap("ids",ids)).list();
            for(T t : entities){
                String id =((IEntity)t).getId();
                entityMap.put(id, t);
            }
        }
        for(String id : orderedIds){
            if(entityMap.containsKey(id)){
                result.add(entityMap.get(id));
            }
        }
        
        return result;
    }
} 

使用

java 复制代码
//更多使用自行参考
@Repository
public class CommonRepositoryHibernate extends JpaHibernateRepository {

    //构建当前年获取数据
    public List<DateRec> getHolidayByYear(Date year) {
        String hql = "select d from " + DateRec.class.getName() + " d where d.valid = 1 and dateTime >= :dateTime ";
        List<DateRec> holidays = this.createHQLQueryByMapParams(DateRec.class, hql, Utils.buildMap("dateTime", year)).list();
        if (holidays == null) holidays = new ArrayList<>();
        return holidays;
    }

    //根据主键获取实体对象
    public DateRec getDateRecById(String id) {
        DateRec dateRec = this.safeGet(DateRec.class, id);
        return dateRec;
    }

    //根据前端参数构建sql
    public List<DateRec> getDateRecBy(Map<String, Object> params) {
        StringBuffer sql = new StringBuffer();
        sql.append(" select id from t_date_rec d where d.valid = 1 ");
        if (params.get("ids") != null) {
            sql.append(" and d.id in (:ids) ");
        }
        if (params.get("dateTypes") != null) {
            sql.append(" and d.date_type in (:dateTypes) ");
        }
        List<String> ids = this.createSQLQueryByMapParams(sql.toString(), params).list();
        if (CollectionUtils.isEmpty(ids)) return new ArrayList<>();
        List<DateRec> recs = this.findByOrderedIds(DateRec.class, ids);
        if (recs == null) recs = new ArrayList<>();
        return recs;

    }

}
相关推荐
鼠爷ねずみ11 小时前
SpringCloud前后端整体开发流程-以及技术总结文章实时更新中
java·数据库·后端·spring·spring cloud
从心归零14 小时前
springboot-jpa的批量更新方法
java·spring boot·spring
LiamTuc14 小时前
Hibernate‌
spring
小许学java17 小时前
Spring事务和事务传播机制
java·数据库·spring·事务
这是程序猿19 小时前
基于java的ssm框架经典电影推荐网站
java·开发语言·spring boot·spring·经典电影推荐网站
jiayong2319 小时前
海外求职平台与策略指南
java·spring
SadSunset19 小时前
(37)全注解式开发AOP
java·spring
子超兄20 小时前
Bean生命周期
java·spring
Mr.朱鹏20 小时前
超时订单处理方案实战指南【完整版】
java·spring boot·redis·spring·rabbitmq·rocketmq·订单
Lisonseekpan21 小时前
RBAC 基于角色的访问控制模型详解与实践指南
java·服务器·网络·后端·spring·log4j