【发布时间】:2018-10-04 02:03:55
【问题描述】:
我正在编写一个使用 JpaRepository 接口的 Spring Boot 应用程序。在我尝试编写一些对象的服务方法中,对两个存储库的 save() 方法的调用似乎没有做任何事情,但也没有抛出任何异常。
我正在使用带有 ISAM 表和 MySQL5Dialect 的 mysql。
这是我的实体: 预订
package com.bigbadcon.dataservices.entity.eventManager;
import com.bigbadcon.dataservices.entity.wordpress.Users;
import javax.persistence.*;
import java.math.BigDecimal;
import java.sql.Timestamp;
@Entity
@Table(name = "bookings", schema = "redacted", catalog = "")
public class Bookings {
private Long bookingId;
private Integer bookingSpaces;
private String bookingComment;
private Timestamp bookingDate;
private Integer bookingStatus;
private BigDecimal bookingPrice;
private String bookingMeta;
private BigDecimal bookingTaxRate;
private BigDecimal bookingTaxes;
private Users user;
private Events event;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "booking_id")
public Long getBookingId() {
return bookingId;
}
public void setBookingId(Long bookingId) {
this.bookingId = bookingId;
}
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "person_id", referencedColumnName = "ID")
public Users getUser() {
return this.user;
}
public void setUser(Users user) {
this.user = user;
}
@ManyToOne
@JoinColumn(name = "event_id")
public Events getEvent() {
return this.event;
}
public void setEvent(Events event) {
this.event = event;
}
@Basic
@Column(name = "booking_spaces")
public Integer getBookingSpaces() {
return bookingSpaces;
}
public void setBookingSpaces(Integer bookingSpaces) {
this.bookingSpaces = bookingSpaces;
}
@Basic
@Column(name = "booking_comment", columnDefinition = "TEXT")
public String getBookingComment() {
return bookingComment;
}
public void setBookingComment(String bookingComment) {
this.bookingComment = bookingComment;
}
@Basic
@Column(name = "booking_date")
public Timestamp getBookingDate() {
return bookingDate;
}
public void setBookingDate(Timestamp bookingDate) {
this.bookingDate = bookingDate;
}
@Basic
@Column(name = "booking_status", columnDefinition = "TINYINT", length = 1)
public Integer getBookingStatus() {
return bookingStatus;
}
public void setBookingStatus(Integer bookingStatus) {
this.bookingStatus = bookingStatus;
}
@Basic
@Column(name = "booking_price")
public BigDecimal getBookingPrice() {
return bookingPrice;
}
public void setBookingPrice(BigDecimal bookingPrice) {
this.bookingPrice = bookingPrice;
}
@Basic
@Lob
@Column(name = "booking_meta")
public String getBookingMeta() {
return bookingMeta;
}
public void setBookingMeta(String bookingMeta) {
this.bookingMeta = bookingMeta;
}
@Basic
@Column(name = "booking_tax_rate")
public BigDecimal getBookingTaxRate() {
return bookingTaxRate;
}
public void setBookingTaxRate(BigDecimal bookingTaxRate) {
this.bookingTaxRate = bookingTaxRate;
}
@Basic
@Column(name = "booking_taxes")
public BigDecimal getBookingTaxes() {
return bookingTaxes;
}
public void setBookingTaxes(BigDecimal bookingTaxes) {
this.bookingTaxes = bookingTaxes;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Bookings that = (Bookings) o;
if (bookingId != null ? !bookingId.equals(that.bookingId) : that.bookingId != null) return false;
if (bookingSpaces != null ? !bookingSpaces.equals(that.bookingSpaces) : that.bookingSpaces != null)
return false;
if (bookingComment != null ? !bookingComment.equals(that.bookingComment) : that.bookingComment != null)
return false;
if (bookingDate != null ? !bookingDate.equals(that.bookingDate) : that.bookingDate != null) return false;
if (bookingStatus != null ? !bookingStatus.equals(that.bookingStatus) : that.bookingStatus != null)
return false;
if (bookingPrice != null ? !bookingPrice.equals(that.bookingPrice) : that.bookingPrice != null) return false;
if (bookingMeta != null ? !bookingMeta.equals(that.bookingMeta) : that.bookingMeta != null) return false;
if (bookingTaxRate != null ? !bookingTaxRate.equals(that.bookingTaxRate) : that.bookingTaxRate != null)
return false;
if (bookingTaxes != null ? !bookingTaxes.equals(that.bookingTaxes) : that.bookingTaxes != null) return false;
return true;
}
@Override
public int hashCode() {
int result = bookingId != null ? bookingId.hashCode() : 0;
result = 31 * result + (bookingSpaces != null ? bookingSpaces.hashCode() : 0);
result = 31 * result + (bookingComment != null ? bookingComment.hashCode() : 0);
result = 31 * result + (bookingDate != null ? bookingDate.hashCode() : 0);
result = 31 * result + (bookingStatus != null ? bookingStatus.hashCode() : 0);
result = 31 * result + (bookingPrice != null ? bookingPrice.hashCode() : 0);
result = 31 * result + (bookingMeta != null ? bookingMeta.hashCode() : 0);
result = 31 * result + (bookingTaxRate != null ? bookingTaxRate.hashCode() : 0);
result = 31 * result + (bookingTaxes != null ? bookingTaxes.hashCode() : 0);
return result;
}
}
门票预订
import com.bigbadcon.dataservices.entity.wordpress.Users;
import javax.persistence.*;
import java.math.BigDecimal;
import java.sql.Timestamp;
@Entity
@Table(name = "bookings", schema = "redacted", catalog = "")
public class Bookings {
private Long bookingId;
private Integer bookingSpaces;
private String bookingComment;
private Timestamp bookingDate;
private Integer bookingStatus;
private BigDecimal bookingPrice;
private String bookingMeta;
private BigDecimal bookingTaxRate;
private BigDecimal bookingTaxes;
private Users user;
private Events event;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "booking_id")
public Long getBookingId() {
return bookingId;
}
public void setBookingId(Long bookingId) {
this.bookingId = bookingId;
}
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "person_id", referencedColumnName = "ID")
public Users getUser() {
return this.user;
}
public void setUser(Users user) {
this.user = user;
}
@ManyToOne
@JoinColumn(name = "event_id")
public Events getEvent() {
return this.event;
}
public void setEvent(Events event) {
this.event = event;
}
@Basic
@Column(name = "booking_spaces")
public Integer getBookingSpaces() {
return bookingSpaces;
}
public void setBookingSpaces(Integer bookingSpaces) {
this.bookingSpaces = bookingSpaces;
}
@Basic
@Column(name = "booking_comment", columnDefinition = "TEXT")
public String getBookingComment() {
return bookingComment;
}
public void setBookingComment(String bookingComment) {
this.bookingComment = bookingComment;
}
@Basic
@Column(name = "booking_date")
public Timestamp getBookingDate() {
return bookingDate;
}
public void setBookingDate(Timestamp bookingDate) {
this.bookingDate = bookingDate;
}
@Basic
@Column(name = "booking_status", columnDefinition = "TINYINT", length = 1)
public Integer getBookingStatus() {
return bookingStatus;
}
public void setBookingStatus(Integer bookingStatus) {
this.bookingStatus = bookingStatus;
}
@Basic
@Column(name = "booking_price")
public BigDecimal getBookingPrice() {
return bookingPrice;
}
public void setBookingPrice(BigDecimal bookingPrice) {
this.bookingPrice = bookingPrice;
}
@Basic
@Lob
@Column(name = "booking_meta")
public String getBookingMeta() {
return bookingMeta;
}
public void setBookingMeta(String bookingMeta) {
this.bookingMeta = bookingMeta;
}
@Basic
@Column(name = "booking_tax_rate")
public BigDecimal getBookingTaxRate() {
return bookingTaxRate;
}
public void setBookingTaxRate(BigDecimal bookingTaxRate) {
this.bookingTaxRate = bookingTaxRate;
}
@Basic
@Column(name = "booking_taxes")
public BigDecimal getBookingTaxes() {
return bookingTaxes;
}
public void setBookingTaxes(BigDecimal bookingTaxes) {
this.bookingTaxes = bookingTaxes;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Bookings that = (Bookings) o;
if (bookingId != null ? !bookingId.equals(that.bookingId) : that.bookingId != null) return false;
if (bookingSpaces != null ? !bookingSpaces.equals(that.bookingSpaces) : that.bookingSpaces != null)
return false;
if (bookingComment != null ? !bookingComment.equals(that.bookingComment) : that.bookingComment != null)
return false;
if (bookingDate != null ? !bookingDate.equals(that.bookingDate) : that.bookingDate != null) return false;
if (bookingStatus != null ? !bookingStatus.equals(that.bookingStatus) : that.bookingStatus != null)
return false;
if (bookingPrice != null ? !bookingPrice.equals(that.bookingPrice) : that.bookingPrice != null) return false;
if (bookingMeta != null ? !bookingMeta.equals(that.bookingMeta) : that.bookingMeta != null) return false;
if (bookingTaxRate != null ? !bookingTaxRate.equals(that.bookingTaxRate) : that.bookingTaxRate != null)
return false;
if (bookingTaxes != null ? !bookingTaxes.equals(that.bookingTaxes) : that.bookingTaxes != null) return false;
return true;
}
@Override
public int hashCode() {
int result = bookingId != null ? bookingId.hashCode() : 0;
result = 31 * result + (bookingSpaces != null ? bookingSpaces.hashCode() : 0);
result = 31 * result + (bookingComment != null ? bookingComment.hashCode() : 0);
result = 31 * result + (bookingDate != null ? bookingDate.hashCode() : 0);
result = 31 * result + (bookingStatus != null ? bookingStatus.hashCode() : 0);
result = 31 * result + (bookingPrice != null ? bookingPrice.hashCode() : 0);
result = 31 * result + (bookingMeta != null ? bookingMeta.hashCode() : 0);
result = 31 * result + (bookingTaxRate != null ? bookingTaxRate.hashCode() : 0);
result = 31 * result + (bookingTaxes != null ? bookingTaxes.hashCode() : 0);
return result;
}
}
这里是 Bookings 实体的 JpaRepository:
import com.bigbadcon.dataservices.entity.eventManager.Bookings;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Repository
@Transactional
public interface BookingsRepositoryInterface extends JpaRepository<Bookings, Long> {
@Query(value = "SELECT b from Bookings b where b.user.id = ?1 AND b.event.eventName not like 'Verify % badge%' " +
"AND FUNCTION('YEAR', b.event.eventStartDate) = ?2 and b.bookingStatus = 1")
List<Bookings> findForUser(Long userId, Integer filterYear);
@Query(value = "SELECT b from Bookings b where b.event.eventId= ?1 and b.bookingSpaces = 1 and b.bookingStatus = 1")
List<Bookings> findForEvent(Long eventId);
}
对于 TicketsBookings 实体:
package com.bigbadcon.dataservices.repository.eventManager.interfaces;
import com.bigbadcon.dataservices.entity.eventManager.TicketsBookings;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Repository
@Transactional
public interface TicketsBookingsRepositoryInterface extends JpaRepository<TicketsBookings, Long> {
@Query(value = "SELECT tb from TicketsBookings tb where tb.bookingId.event.eventId = ?1 ")
List<TicketsBookings> findForEvent(Long eventId);
}
最后,这是不正常的方法:
@Service
public class BookingsService {
@Autowired
private BookingsRepositoryInterface bookingsDAO;
@Autowired
private TicketsRepositoryInterface ticketsDAO;
@Autowired
private TicketsBookingsRepositoryInterface ticketsBookingsDAO;
@Autowired
private EventsRepositoryInterface eventsDAO;
@Autowired
private UsersRepositoryInterface usersDAO;
@Autowired
private OptionsRepositoryInterface optionsDAO;
@Autowired
private EmailService emailService;
@Autowired
private BookingsService bookingsService;
@Autowired
private SecurityService securityService;
@PersistenceContext
EntityManager em;
private static final Logger log = LoggerFactory.getLogger(BookingsService.class);
public HashMap<String, Boolean> stateTable;
private Integer statusBooked = 1;
private Integer statusCancelled = 2;
private Integer statusDeleted = 3;
/**
* <p>This method is only to be called by the game registration consumer. It assumes the UI has done some sort
* of check that the user isn't already booked in the game. The WP UI does this.</p>
* <p>Admin users can call the addPlayerToGame method instead.</p>
* @param eventId
* @param userId
* @param uuid
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createBooking(Long eventId, Long userId, String uuid) {
Events event = eventsDAO.findOne(eventId);
log.info("Event found: " + event.toString());
Users user = usersDAO.findOne(userId);
log.info("User found: " + user.toString());
Timestamp now = new Timestamp(System.currentTimeMillis());
Tickets ticket = ticketsDAO.findTicketByEventId(event.getEventId());
if (ticket == null) {
log.info("Event " + event.getEventId() + " is not open for registration yet.");
return;
}
log.info("Found ticket: " + ticket.toString());
List<Bookings> bookingsList = bookingsDAO.findForEvent(event.getEventId());
for (Bookings booking : bookingsList) {
if (booking.getUser().getId().equals(userId)) {
log.info("User " + booking.getUser().getDisplayName() + " has already signed up for event id " + event.getEventId());
return;
}
}
Integer bookingSpaces = 1;
Integer bookingStatus = 1;
if (bookingsList.size() >= ticket.getTicketSpaces()) {
bookingSpaces = 0;
bookingStatus = 4;
}
Bookings booking = new Bookings();
booking.setEvent(event);
booking.setUser(user);
booking.setBookingSpaces(bookingSpaces);
booking.setBookingDate(now);
booking.setBookingStatus(bookingStatus);
booking.setBookingMeta("a:0:{}");
bookingsDAO.save(booking);
TicketsBookings ticketBooking = new TicketsBookings();
ticketBooking.setBookingId(booking);
ticketBooking.setTicketId(ticket.getTicketId());
ticketBooking.setTicketBookingSpaces(statusBooked);
ticketBooking.setTicketBookingPrice(new BigDecimal(0));
ticketsBookingsDAO.save(ticketBooking);
//TODO send rejection mail for overbookings
try {
if (bookingStatus.equals(1)) {
emailService.sendEmail(createConfirmedPlayerEmail(user, event));
emailService.sendEmail(createConfirmedHostEmail(user, event));
bookingsService.stateTable.put(uuid, new Boolean(true));
}
else {
bookingsService.stateTable.put(uuid, new Boolean(false));
}
}
catch (Exception e) {
e.printStackTrace();
}
}
根据我在该主题上阅读的内容,使用 @Transactional 注释的方法应在方法返回后提交。当我执行该方法时,它实际上会打印到它正在保存的日志中。但是我打开了sql调试,保存的时候不打印任何sql语句。 (它会在选择时打印 sql 语句。)
该方法在进行上述选择时没有问题,只有后半部分的保存。不会引发异常,但数据不会出现在实体正在使用的表中。每次保存的日志中打印的唯一行如下所示:
o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
我不确定这是否相关,但此调用是由侦听器类为 jms 队列进行的。
我的问题是,我是否遗漏了以这种方式启用持久数据的任何东西?根据我的阅读,@Transactional 自动将 readOnly 设置为 true,并且我的 IDE 确认了这一点。我错过了什么重要的事情吗?
【问题讨论】:
-
只是我的两分钱,MyISAM 不是事务性的,请参阅:stackoverflow.com/questions/8036005/…,也许你应该尝试一下 InnoDB
-
可能与问题无关,但
@Transactional属于服务而非存储库。 -
我不敢相信我忘记了 MyISAM 不是事务性的。 磕头
-
我修改了所有表以使用 InnoDB,并从所有存储库接口中删除了 @Transactional 注释,并将属性文件中的方言更改为 MySQL5InnoDBDialect。行为没有变化。
-
您确认方法执行确实到达了保存部分吗?因为在所有这些预订和机票保存之前,有几个 ifs 最终会返回
标签: java spring-mvc spring-boot jpa spring-data-jpa