SpringBoot 2.0适配:JPA接口save/delete兼容Long与ArrayList问题
Hey there! Let's work through adapting your Spring Boot 1.3 code to 2.0, focusing on the JPA save/delete method changes and supporting Long IDs and ArrayLists. I’ve helped tons of developers navigate these exact shifts, so let’s break it down step by step.
First, let's tweak your HotelBooking entity to align with Spring Boot 2.x best practices. The key change here is switching the primitive long ID to the wrapper type Long—this plays nicer with Spring Data JPA 2.x's use of Optional for null-safe operations, avoiding accidental auto-unboxing NPEs.
@Entity public class HotelBooking { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; // Switch from long to Long for null-safe compatibility // Example additional fields (adjust to your actual model) private String hotelName; private Double pricePerNight; private int numberOfNights; // Default constructor (required by JPA) public HotelBooking() {} // Custom constructor for creating bookings without ID public HotelBooking(String hotelName, Double pricePerNight, int numberOfNights) { this.hotelName = hotelName; this.pricePerNight = pricePerNight; this.numberOfNights = numberOfNights; } // Getters and Setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } // Add getters/setters for your other fields here }
In Spring Boot 2.x, CrudRepository still works, but JpaRepository provides more out-of-the-box methods and cleaner alignment with modern JPA practices. The critical method changes here are:
- Replace
delete(Long id)withdeleteById(Long id) - Use
saveAll(Iterable<T>)instead of the deprecatedsave(Iterable<T>)for bulk operations
Here's your updated repository:
import org.springframework.data.jpa.repository.JpaRepository; public interface HotelBookingRepository extends JpaRepository<HotelBooking, Long> { // No custom save/delete methods needed—JpaRepository provides all we need! }
Now let's update your service to use the new repository methods, handle ArrayLists for bulk saves, and add null-safe checks with Optional:
import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.Optional; @Service public class HotelBookingService { private final HotelBookingRepository bookingRepository; // Constructor injection (Spring Boot 2.x recommends this over @Autowired) public HotelBookingService(HotelBookingRepository bookingRepository) { this.bookingRepository = bookingRepository; } // Save a single booking public HotelBooking saveSingleBooking(HotelBooking booking) { return bookingRepository.save(booking); } // Save an ArrayList of bookings (use saveAll instead of deprecated save(Iterable)) public List<HotelBooking> saveBulkBookings(ArrayList<HotelBooking> bookings) { return bookingRepository.saveAll(bookings); } // Delete a booking by Long ID public void deleteBookingById(Long id) { // Optional: Add a check to throw an error if the ID doesn't exist if (!bookingRepository.existsById(id)) { throw new RuntimeException("Booking with ID " + id + " not found"); } bookingRepository.deleteById(id); } // Fetch a booking by Long ID (returns Optional for null safety) public Optional<HotelBooking> getBookingById(Long id) { return bookingRepository.findById(id); } }
Finally, adjust your controller to expose the updated functionality, handle ArrayList payloads, and return appropriate HTTP status codes:
import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.Optional; @RestController @RequestMapping("/api/bookings") public class HotelBookingController { private final HotelBookingService bookingService; public HotelBookingController(HotelBookingService bookingService) { this.bookingService = bookingService; } // Create a single booking @PostMapping public ResponseEntity<HotelBooking> createBooking(@RequestBody HotelBooking booking) { HotelBooking savedBooking = bookingService.saveSingleBooking(booking); return new ResponseEntity<>(savedBooking, HttpStatus.CREATED); } // Bulk create bookings from an ArrayList @PostMapping("/bulk") public ResponseEntity<List<HotelBooking>> createBulkBookings(@RequestBody ArrayList<HotelBooking> bookings) { List<HotelBooking> savedBookings = bookingService.saveBulkBookings(bookings); return new ResponseEntity<>(savedBookings, HttpStatus.CREATED); } // Delete a booking by ID @DeleteMapping("/{id}") public ResponseEntity<Void> deleteBooking(@PathVariable Long id) { bookingService.deleteBookingById(id); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } // Get a booking by ID @GetMapping("/{id}") public ResponseEntity<HotelBooking> getBooking(@PathVariable Long id) { Optional<HotelBooking> booking = bookingService.getBookingById(id); return booking.map(ResponseEntity::ok) .orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND)); } }
- Deprecated Methods: The old
save(Iterable)method is marked as deprecated in 2.x—always usesaveAll()for bulk operations instead. - Null Safety: Using
Optionalfor fetch operations prevents null pointer exceptions when a requested ID doesn't exist. - ID Validation: Adding an
existsById(id)check before deletion lets you throw meaningful errors instead of silent failures (customize the exception type to match your app's needs).
内容的提问来源于stack exchange,提问作者Angel

