You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring中基于可选参数查询MySQL酒店记录的技术咨询

在Spring中实现带可选参数的酒店数据查询

嘿,我来帮你搞定Spring里带可选参数的酒店数据查询问题!结合你提到的MySQL的Hotel和Region表(一对多关系),我整理了几种常用的实现方案,你可以根据自己的项目技术栈来选:

方案一:Spring Data JPA + Specification(最常用的原生方案)

如果你的项目用Spring Data JPA,Specification是实现动态查询的绝佳选择,它能帮你按需拼接查询条件,不用写硬编码SQL。

第一步:配置实体类关联

首先得让Hotel和Region实体建立关联,这样才能支持跨表查询:

@Entity
@Table(name = "Hotel")
public class Hotel {
    @Id
    @Column(name = "hotel_id")
    private Long hotelId;
    private String name;
    @Column(name = "group_id")
    private Long groupId;
    private String status;
    @Column(name = "brand_id")
    private String brandId;
    @Column(name = "no_rooms")
    private Integer noRooms;
    @Column(name = "region_id")
    private Long regionId;

    // 关联Region表,一对多关系的多端用@ManyToOne
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "region_id", insertable = false, updatable = false)
    private Region region;

    // getter、setter省略
}

@Entity
@Table(name = "Region")
public class Region {
    @Id
    @Column(name = "region_id")
    private Long regionId;
    @Column(name = "region_name")
    private String regionName;
    @Column(name = "region_code")
    private String regionCode;
    @Column(name = "region_manager")
    private String regionManager;

    // getter、setter省略
}

第二步:定义Repository

让HotelRepository继承JpaSpecificationExecutor,这样就能用Specification查询了:

public interface HotelRepository extends JpaRepository<Hotel, Long>, JpaSpecificationExecutor<Hotel> {
}

第三步:在Service中构建动态查询

在Service里根据传入的可选参数,逐一添加查询条件:

@Service
public class HotelService {

    @Autowired
    private HotelRepository hotelRepository;

    public List<Hotel> searchHotels(Long groupId, String brandId, String status, String regionName) {
        return hotelRepository.findAll((root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            // 处理group_id可选参数:不为空才添加条件
            if (groupId != null) {
                predicates.add(cb.equal(root.get("groupId"), groupId));
            }

            // 处理brand_id可选参数:非空非空串才添加
            if (brandId != null && !brandId.isBlank()) {
                predicates.add(cb.equal(root.get("brandId"), brandId));
            }

            // 处理status可选参数
            if (status != null && !status.isBlank()) {
                predicates.add(cb.equal(root.get("status"), status));
            }

            // 处理Region关联条件:比如按地区名称模糊查询
            if (regionName != null && !regionName.isBlank()) {
                Join<Hotel, Region> regionJoin = root.join("region", JoinType.INNER);
                predicates.add(cb.like(regionJoin.get("regionName"), "%" + regionName + "%"));
            }

            // 把所有条件用AND拼接起来
            return cb.and(predicates.toArray(new Predicate[0]));
        });
    }
}

方案二:QueryDSL(更简洁的类型安全方案)

如果你觉得Specification的代码有点繁琐,QueryDSL能让动态查询的代码更简洁,而且是类型安全的,不用担心字段名写错。

第一步:引入依赖(Maven为例)

<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
    <version>5.0.0</version>
</dependency>
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <version>5.0.0</version>
    <scope>provided</scope>
</dependency>

还要加个插件生成QueryDSL的Q类(基于实体类自动生成的查询类):

<plugin>
    <groupId>com.mysema.maven</groupId>
    <artifactId>apt-maven-plugin</artifactId>
    <version>1.1.3</version>
    <executions>
        <execution>
            <goals>
                <goal>process</goal>
            </goals>
            <configuration>
                <outputDirectory>target/generated-sources/java</outputDirectory>
                <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
            </configuration>
        </execution>
    </executions>
</plugin>

第二步:修改Repository

让HotelRepository继承QuerydslJpaRepository

public interface HotelRepository extends JpaRepository<Hotel, Long>, QuerydslJpaRepository<Hotel, Long> {
}

第三步:Service层实现动态查询

用QueryDSL的BooleanBuilder来拼接条件,代码清爽很多:

@Service
public class HotelService {

    @Autowired
    private HotelRepository hotelRepository;

    // 自动生成的Q类,用来做类型安全查询
    private final QHotel qHotel = QHotel.hotel;
    private final QRegion qRegion = QRegion.region;

    public List<Hotel> searchHotels(Long groupId, String brandId, String status, String regionName) {
        BooleanBuilder builder = new BooleanBuilder();

        if (groupId != null) {
            builder.and(qHotel.groupId.eq(groupId));
        }
        if (brandId != null && !brandId.isBlank()) {
            builder.and(qHotel.brandId.eq(brandId));
        }
        if (status != null && !status.isBlank()) {
            builder.and(qHotel.status.eq(status));
        }
        if (regionName != null && !regionName.isBlank()) {
            builder.and(qHotel.region.regionName.like("%" + regionName + "%"));
        }

        return hotelRepository.findAll(builder);
    }
}

方案三:MyBatis动态SQL(适合复杂SQL场景)

如果你的项目用MyBatis,直接用XML里的<if>标签就能轻松实现动态条件拼接,灵活性最高。

第一步:定义Mapper接口

public interface HotelMapper {
    List<Hotel> searchHotels(@Param("groupId") Long groupId,
                             @Param("brandId") String brandId,
                             @Param("status") String status,
                             @Param("regionName") String regionName);
}

第二步:编写Mapper XML的动态SQL

<if>标签判断参数是否存在,动态拼接WHERE条件:

<select id="searchHotels" resultType="com.example.entity.Hotel">
    SELECT h.*
    FROM Hotel h
    LEFT JOIN Region r ON h.region_id = r.region_id
    WHERE 1=1
    <if test="groupId != null">
        AND h.group_id = #{groupId}
    </if>
    <if test="brandId != null and brandId != ''">
        AND h.brand_id = #{brandId}
    </if>
    <if test="status != null and status != ''">
        AND h.status = #{status}
    </if>
    <if test="regionName != null and regionName != ''">
        AND r.region_name LIKE CONCAT('%', #{regionName}, '%')
    </if>
</select>

一些注意事项

  • 参数校验:处理可选参数前一定要做非空/非空串校验,避免无效查询或空指针异常
  • 关联查询性能:如果用JPA的关联查询,建议用懒加载(FetchType.LAZY),避免不必要的表关联导致性能下降
  • 模糊查询优化:如果经常用regionName做模糊查询,建议给region.region_name字段加全文索引,或者用MySQL的LIKE优化技巧

内容的提问来源于stack exchange,提问作者Uditha

火山引擎 最新活动