FindByObject returns null even when record is in the db - spring-boot

In a springboot application I create a Game, with a list-of-cards converted in a large String.
The Game is related to a Player.
When I try to lookup the Game for a Player, the Springboot Hibernate app returns NULL . Why??!??
Now I want to create an endpoint to show a players game.
I cannot get it working, looking for a solution for days now...
The test I wrote shows the players, with game_ids, but when I try to FindByPlayer, the repository returns: null
The console shows:
=====================1
player = Player [id=234, game=Game [id=232, gameCards=nl.hu.bep2.casino.blackjack.domain.GameCards#c5092df, gameState=playing, numberOfDecks=1], user=nl.hu.bep2.casino.security.domain.User#1837c802]
=====================2
2023-02-17 18:33:45.333 DEBUG 2216 --- [nio-8080-exec-3] org.hibernate.SQL : select game0_.id as id1_2_, game0_.current_move as current_2_2_, game0_.game_cards as game_car3_2_, game0_.game_state as game_sta4_2_, game0_.number_of_decks as number_o5_2_ from game game0_ left outer join player player1_ on game0_.id=player1_.game_id where player1_.id=?
====================3
player =Player [id=234, game=Game [id=232, gameCards=nl.hu.bep2.casino.blackjack.domain.GameCards#c5092df, gameState=playing, numberOfDecks=1], user=nl.hu.bep2.casino.security.domain.User#1837c802]
speler: 234 heeft geen game
and postman shows:[ null ]
I use the following Classes:
Game
#Transactional
#Entity
#JsonIgnoreProperties({"player","dealer"})
public class Game {
#Id
#GeneratedValue
private long id;
#Convert(converter = CardListConverter.class)
#Column(length = 20000)
private GameCards gameCards;
#Enumerated(EnumType.STRING)
private GameState gameState;
#OneToOne(mappedBy="game", cascade = CascadeType.ALL)
private Player player;
#OneToOne(mappedBy="game", cascade = CascadeType.ALL)
private Dealer dealer;
private Move current_move;
private Integer numberOfDecks;
public Game() {
}
Player
#Transactional
#Entity
#JsonIgnoreProperties("game")
public class Player extends Hand implements Serializable{
#Id
#GeneratedValue
private long id;
//#JsonManagedReference
#OneToOne(fetch=FetchType.EAGER)
#JoinColumn(name="game_id")
private Game game;
#ManyToOne(fetch = FetchType.EAGER)
private User user; // niet via id koppelen aan een object, user heeft de link naar gepersisteerde chips. dit private object wordt in de applicatielaag gevuld met de chips van de user
//dependency injection van chips in player in applicatielaag, blackjackservice, startgame
public Player(){
}
public Player(User user, Game game) {
super();
this.user = user;
this.game=game;
}
GameRepository
public interface GameRepository extends JpaRepository<Game, Long>{
Game findByPlayer(Player player);
}
GamesService
#Service
public class GamesService {
#Autowired
private GameRepository gameRepository;
#Autowired
private UserService userService;
#Autowired
private PlayerService playerService;
public GamesService(){
}
public List<Game> GetGamesByUsername(String username) {
List<Game> gameList = new ArrayList<>();
List<Player> players = this.playerService.GetPlayerByUsername(username);
for
( Player p : players) {
System.out.println("=====================1");
System.out.println("player = "+ p);
System.out.println("=====================2");
Game game = this.gameRepository.findByPlayer(p);
gameList.add(game);
System.out.println("====================3"); System.out.println("player ="+
p);
if (game != null) { System.out.println("speler: "+ p.getId() +
" /n vinden we game " + game.getId()); }else{
System.out.println("speler: "+ p.getId() + " heeft geen game"); }
}
return gameList;
}
}
GamesController
#RestController
#RequestMapping("/game")
public class GamesController {
private final GamesService service;
// SameService injecteren, en service noemen
public GamesController(GamesService service) {
this.service = service;
}
#PostMapping("/showgames")
#ResponseBody
public List<Game> getGames(Authentication authentication, UserService userService){
UserProfile profile = (UserProfile) authentication.getPrincipal();
try {
List<Game> gameList = new ArrayList<>();
String username = profile.getUsername();
gameList = this.service.GetGamesByUsername(username);
return gameList;
} catch (NegativeNumberException exception) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, exception.getMessage());
}
}
}
Also there is a User connected to the player, that works fine
Playerservice works fine:
#Service
public class PlayerService {
#Autowired
private PlayerRepository playerRepository;
#Autowired
private UserService userService;
public List<Player> GetPlayerByUsername(String username) {
User user = userService.loadUserByUsername(username);
List<Player> players = playerRepository.findByUser(user);
return players;
}

If player has game_id, then you may try to reach Game by id as shown below:
public interface GameRepository extends JpaRepository<Game, Long>{
Optional<Game> findById(Long id);
}
Then pass the player.getGameId() to the repository instead of Player.

Related

Problems with cascading in spring boot

I want to add an address to a person, when I'm saving the address but it doesn't work. I have already tried all variants of cascading. Could anybody help me to solve this problem? In Entities, I left only fields related to my question.
Address Entity
public class Address {
#OneToOne(mappedBy = "address", cascade = CascadeType.ALL)
private Visitor visitor;
}
Visitor Entity
public class Visitor {
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "address_id")
private Address address;
}
Address Service
public class AddressService {
private final AddressRepository addressRepository;
private final VisitorRepository visitorRepository;
public ResponseEntity<Address> saveAddress(Address address, String visitorId) {
Visitor visitor = visitorRepository.findById(visitorId).orElseThrow(() -> new VisitorNotFoundException("Visitor with ID: " + visitorId + " not found"));
Address addressToSave = Address.builder()
.addressLine(address.getAddressLine())
.city(address.getCity())
.zip(address.getZip())
.visitor(visitor)
.build();
addressRepository.save(addressToSave);
return ResponseEntity.created(
ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(addressToSave.getId())
.toUri())
.body(addressToSave);
}
}
VisitorService
public class VisitorService {
private final VisitorRepository visitorRepository;
public ResponseEntity<Visitor> saveVisitor(Visitor visitor) {
Visitor visitorToSave = Visitor.builder()
.address(visitor.getAddress())
.name(visitor.getName())
.phoneNumber(visitor.getPhoneNumber())
.build();
visitorRepository.save(visitorToSave);
return ResponseEntity.created(
ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(visitorToSave.getId())
.toUri())
.body(visitorToSave);
}
}
Have you added #Autowired annotation as like this or not
IN AddressService
#Autowired
private final AddressRepository addressRepository;
#Autowired
private final VisitorRepository visitorRepository;
In VisitorService
#Autowired
private final VisitorRepository visitorRepository;

Spring boot JPARepository doesn't execute save

Im building a simple app in spring boot and having problems with saving an entity to the db. The bank account is saved normally, but the transaction is not being saved.
The weird thing is that there are no errors or anything. On the first transactionRepository call the method is exited and the app continues normally. I don't get any message in the console or and didn't find any problems using debugging.
Here is the makeTransaction() method, which executes normally until transactionRepository.save(...)
public void makeTransaction(Iban sourceIban, Iban destinationIban, double amount)
throws Exception {
BankAccountEntity sourceAccount = getBankAccountByIban(sourceIban);
BankAccountEntity destinationAccount = getBankAccountByIban(destinationIban);
Date currentDate = new Date();
TransactionEntity sourceTransaction = new TransactionEntity(sourceAccount, sourceIban, destinationIban, currentDate, amount);
TransactionEntity destinationTransaction = new TransactionEntity(destinationAccount, sourceIban, destinationIban, currentDate, amount);
if (sourceAccount.getAmountDeductible() < amount || amount < 0) { // TODO amount checker
throw new Exception(ILLEGAL_TRANSACTION_AMOUNT_TEXT); // TODO change exception type
}
sourceAccount.reduceBalance(amount);
sourceAccount.addTransaction(sourceTransaction);
destinationAccount.increaseBalance(amount);
destinationAccount.addTransaction(destinationTransaction);
bankAccountRepository.save(sourceAccount);
bankAccountRepository.save(destinationAccount);
transactionRepository.save(sourceTransaction);
transactionRepository.saveAndFlush(destinationTransaction);
}
The class has the repositories initialized as follows
private final BankAccountRepository bankAccountRepository;
private final TransactionRepository transactionRepository;
public BankAccountService(#Autowired BankAccountRepository bankAccountRepository,
#Autowired TransactionRepository transactionRepository) {
this.bankAccountRepository = bankAccountRepository;
this.transactionRepository = transactionRepository;
}
The BankAccountRepository looks like this
public interface BankAccountRepository extends JpaRepository<BankAccountEntity, UUID> {
Optional<BankAccountEntity> findByIban(String iban);
}
And the TransactionRepository like this
public interface TransactionRepository extends JpaRepository<TransactionEntity, UUID> {}
The TransactionEntity looks like this
#Entity
#Table(name = "transactions")
public class TransactionEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "transaction_id", nullable = false)
#Type(type = "uuid-char")
private Long transactionId;
private String sourceIban;
private String destinationIban;
private long dateExecuted;
private double amount;
#ManyToOne
#JoinColumn(name = "bank_account_id")
private BankAccountEntity bankAccount;
public TransactionEntity() {
}
public TransactionEntity(BankAccountEntity bankAccount, Iban sourceIban, Iban destinationIban, Date dateExecuted, double amount) {
this.bankAccount = bankAccount;
this.sourceIban = sourceIban.toString();
this.destinationIban = destinationIban.toString();
this.amount = amount;
this.dateExecuted = dateExecuted.getTime();
}
public BankAccountEntity getBankAccount() {
return bankAccount;
}
public void setBankAccount(BankAccountEntity bankAccount) {
this.bankAccount = bankAccount;
}
public Long getTransactionId() {
return transactionId;
}
public void setTransactionId(Long transactionId) {
this.transactionId = transactionId;
}
}

Spring Controller Returns Object Incompletely

There are three classes (Course, Lesson, User).
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "usr")
#Data
public class User extends RepresentationModel<User> implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstname;
private String lastname;
private String username;
private String password;
#ElementCollection(targetClass = ERole.class, fetch = FetchType.EAGER)
#CollectionTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"))
#Enumerated(EnumType.STRING)
private Set<ERole> roles;
}
#Data
#Entity
#NoArgsConstructor
public class Lesson extends RepresentationModel<Lesson> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String startTime;
private String endTime;
private String dayOfWeek;
#ManyToOne
private User teacher;
}
#EqualsAndHashCode(callSuper = true)
#Data
#Entity
public class Course extends RepresentationModel<Course> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Date startDate;
private Date endDate;
private String name;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<User> teachers;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<User> students;
private String description;
#ManyToMany(cascade = CascadeType.ALL)
private Set<Lesson> lessons;
}
And also RestController (CoursesController). When accessing the server at /courses, I get the correct server response with all fields
.
#RestController
#RequestMapping("/courses")
public class CoursesController {
private final CourseService courseService;
private final UserService userService;
private final LessonService lessonService;
#Autowired
public CoursesController(CourseService courseService, UserService userService, LessonService lessonService) {
this.courseService = courseService;
this.userService = userService;
this.lessonService = lessonService;
}
#GetMapping
#Operation(
summary = "getAllCourses",
description = "Returns all available courses"
)
public ResponseEntity<Page<Course>> getAllCourses(#PageableDefault(sort = "id", size = 5) Pageable pageable) {
try {
Page<Course> coursePage = courseService.findAll(pageable);
for (Course course : coursePage.getContent())
course.add(linkTo(methodOn(CoursesController.class).getCourse(course.getId().toString())).withSelfRel());
return ResponseEntity.ok(courseService.findAll(pageable));
}
catch (Exception e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
#GetMapping("/{course-id}")
#Operation(
summary = "getCourse",
description = "Returns course by ID"
)
public ResponseEntity<Course> getCourse(#PathVariable ("course-id") String courseId) {
try {
Course course = courseService.getCourseById(courseId);
course.add(linkTo(methodOn(CoursesController.class).getCourse(courseId)).withSelfRel());
return ResponseEntity.ok(course);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
}
Why, when requesting a course by ID (GET /courses/{id}), does Spring return an incomplete object (despite the fact that I manually added several teachers, students and lessons)?
I need to get all the fields of my object.
My CourseRepository below.
#Repository
#Transactional
public interface CourseRepository extends JpaRepository<Course, Long> {
}
My CourseService below.
#Service
public class CourseService {
private final CourseRepository courseRepository;
private final LessonRepository lessonRepository;
private final UserRepository userRepository;
#Autowired
public CourseService(CourseRepository courseRepository, LessonRepository lessonRepository, UserRepository userRepository) {
this.courseRepository = courseRepository;
this.lessonRepository = lessonRepository;
this.userRepository = userRepository;
}
public Page<Course> findAll(Pageable pageable) {
return courseRepository.findAll(pageable);
}
public Course createCourse(CourseDto courseDto) {
Course course = new Course(courseDto.getStartDate(), courseDto.getEndDate(), courseDto.getName(), courseDto.getDescription());
return courseRepository.saveAndFlush(course);
}
public Optional<Course> getCourseById(String id) {
return courseRepository.findById(Long.parseLong(id));
}
public Course updateCourse(CourseDto courseDto, String id) {
Course course = courseRepository.findById(Long.parseLong(id)).get();
course.setStartDate(courseDto.getStartDate());
course.setEndDate(courseDto.getEndDate());
course.setName(courseDto.getName());
course.setDescription(courseDto.getDescription());
return courseRepository.saveAndFlush(course);
}
public Page<Lesson> getLessonsByCourse(String courseId, Pageable pageable) {
Course course = courseRepository.findById(Long.parseLong(courseId)).get();
return new PageImpl<>(new ArrayList<>(course.getLessons()), pageable, course.getLessons().size());
}
public Course addLesson(String courseId, LessonDto lessonDto) {
Course course = courseRepository.findById(Long.parseLong(courseId)).get();
Lesson lesson = new Lesson();
lesson.setStartTime(lessonDto.getStartTime());
lesson.setEndTime(lessonDto.getFinishTime());
lesson.setDayOfWeek(lessonDto.getDayOfWeek());
lesson.setTeacher(userRepository.getUserById(lessonDto.getTeacherId()));
lessonRepository.saveAndFlush(lesson);
System.out.println(lesson);
course.getLessons().add(lesson);
return courseRepository.saveAndFlush(course);
}
public void deleteCourse(String id) {
courseRepository.deleteById(Long.parseLong(id));
}
}
Which I would (or might) expect as well. I would links to be generated for those additional relationshps (at least normally with Spring Data RESt handling this is what would happen). I wonder what happens if you ditch the RepresentationModel from your JPA model and just expose Course then. As stated you don't really want your JPA and HATEOAS stuff to be intertwined. You want to have a specialized projection/dto to expose. WHy does it work for your findAll. well you aren't adding links to it (although you think it does but your findAll executes twice!).
Removed RepresentationModel from User class.
Thx to #M.Deinum

Spring JPA Transaction ID

I have added an attribute to all my entities - transaction id - which is a sequence generated value that I bump up once in each transaction.
I also store the transaction id with user and start/end times so I have an audit trail for every change in the database.
What is the best way to handle storing a complete graph, where I basically only want to apply the transaction id to those entities that are actually dirty?
I can put a #PrePersist and #PreUpdate on the transaction id column, but how do I retrieve the value for the current transaction id? Is there a way to store and retrieve a value on the transaction object or other JPA controller? Do I need to use a ThreadLocal solution?
Ok, here is what I did. It seems to work in all of the use cases, though I have not done any performance testing, etc. If anyone sees anything that may be non-optimal or may fail in certain situations, please point it out.
Here is the base service class that all #Service implementations must extend:
public class BaseService
{
private final ActivityService activityService;
private final ApplicationEventPublisher applicationEventPublisher;
public static ThreadLocal<Activity> transaction = new ThreadLocal<>();
public BaseService(ActivityService activityService, ApplicationEventPublisher applicationEventPublisher)
{
this.activityService = activityService;
this.applicationEventPublisher = applicationEventPublisher;
}
Object executeWithinActivity(Updater updater)
{
boolean startedLocally = false;
try
{
if (transaction.get() == null)
{
startedLocally = true;
Activity activity = activityService.startTransaction();
transaction.set(activity);
}
return updater.execute(transaction.get());
}
finally
{
if (startedLocally)
{
applicationEventPublisher.publishEvent(new TransactionEvent());
Activity activity = transaction.get();
activityService.endTransaction(activity);
}
}
}
protected interface Updater
{
Object execute (Activity activity);
}
static class TransactionEvent
{
}
}
Activity is the entity that represents the stored transaction id:
#Entity
#Getter #Setter
#Table(name = "transactions", schema = "public", catalog = "euamdb")
public class Activity
{
#Id
#Column(name = "transaction_id", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tx_generator")
#SequenceGenerator(name = "tx_generator", sequenceName = "transaction_seq", allocationSize = 1)
private long transactionId;
#Basic
#Column(name = "user_id", length = 24)
private String userId;
#Basic
#Column(name = "transaction_start")
#CreationTimestamp
private Date transactionStart;
#Basic
#Column(name = "transaction_end")
#UpdateTimestamp
private Date transactionEnd;
#Override
public boolean equals(Object o)
{
if (this == o) return true;
if (!(o instanceof Activity)) return false;
Activity that = (Activity) o;
return transactionId == that.transactionId;
}
#Override
public int hashCode()
{
return Long.hashCode(transactionId);
}
}
ActivityService (which does not extend BaseService):
#Service
public class ActivityService
{
private final ActivityRepository activityRepository;
private final AuthUserService authService;
#Autowired
public ActivityService(ActivityRepository activityRepository, AuthUserService authService)
{
this.activityRepository = activityRepository;
this.authService = authService;
}
#Transactional
public Activity startTransaction()
{
Activity activity = new Activity();
activity.setTransactionStart(new Date());
activity.setUserId(authService.getAuthenticatedUserId());
activityRepository.save(activity);
return activity;
}
#Transactional
public void endTransaction(Activity activity)
{
activity.setTransactionEnd(new Date());
activityRepository.save(activity);
}
}
The base entity class for all entities (excepting Activity):
#MappedSuperclass
#Getter #Setter
public class BaseEntity
{
#Basic
#Column(name = "transaction_id")
private Long transactionId;
#PrePersist
#PreUpdate
public void setupTransaction ()
{
ThreadLocal<Activity> transaction = BaseService.transaction;
Activity activity = transaction.get();
long transactionId = activity.getTransactionId();
setTransactionId(transactionId);
}
}
An example of a service:
#Service
public class OrganizationService extends BaseService
{
private final OrgUserRepository orgUserRepository;
private final UserService userService;
#Autowired
public OrganizationService(ActivityService activityService,
OrgUserRepository orgUserRepository,
UserService userService,
ApplicationEventPublisher applicationEventPublisher)
{
super(activityService, applicationEventPublisher);
this.orgUserRepository = orgUserRepository;
this.userService = userService;
}
#Transactional
public OrgUser save(User user, OrgUser orgUser)
{
return (OrgUser) executeWithinActivity(activity ->
{
orgUser.setUser(userService.save(user));
return orgUserRepository.save(orgUser);
});
}
}
UserService also will extend BaseService and the save(OrgUser) method will also executeWithinActivity.
Finally, the commit listener:
#Component
public class AfterCommitListener
{
#TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
public void doAfterTxComplete(BaseService.TransactionEvent event)
{
BaseService.transaction.remove();
}
}

Hibernate transaction and session with multiple save

Thanks, let me completely change it.
Using:
Spring Boot, Hibernate JPA
I have created a link table with a composite primary key across all 3 columns(event_attendee_link_program)
I used the JPA tools in STS IDE to generate Entities from my tables and it came up with the below code. I removed some of the columns to save space.
EventAttendee.java
#Entity
#Table(name="event_attendee")
#NamedQuery(name="EventAttendee.findAll", query="SELECT e FROM EventAttendee e")
public class EventAttendee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="attendee_id")
private long attendeeId;
//bi-directional many-to-one association to EventAttendeeLinkProgram
#OneToMany(mappedBy="eventAttendee")
private List<EventAttendeeLinkProgram> eventAttendeeLinkPrograms;
public List<EventAttendeeLinkProgram> getEventAttendeeLinkPrograms() {
return this.eventAttendeeLinkPrograms;
}
public void setEventAttendeeLinkPrograms(List<EventAttendeeLinkProgram> eventAttendeeLinkPrograms) {
this.eventAttendeeLinkPrograms = eventAttendeeLinkPrograms;
}
public EventAttendeeLinkProgram addEventAttendeeLinkProgram(EventAttendeeLinkProgram eventAttendeeLinkProgram) {
getEventAttendeeLinkPrograms().add(eventAttendeeLinkProgram);
eventAttendeeLinkProgram.setEventAttendee(this);
return eventAttendeeLinkProgram;
}
public EventAttendeeLinkProgram removeEventAttendeeLinkProgram(EventAttendeeLinkProgram eventAttendeeLinkProgram) {
getEventAttendeeLinkPrograms().remove(eventAttendeeLinkProgram);
eventAttendeeLinkProgram.setEventAttendee(null);
return eventAttendeeLinkProgram;
}
}
EventAttendeeLinkProgram.java
#Entity
#Table(name="event_attendee_link_program")
#NamedQuery(name="EventAttendeeLinkProgram.findAll", query="SELECT e FROM EventAttendeeLinkProgram e")
public class EventAttendeeLinkProgram implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private EventAttendeeLinkProgramPK id;
//bi-directional many-to-one association to EventAttendee
#ManyToOne
#JoinColumn(name="attendee_id", insertable=false, updatable=false)
private EventAttendee eventAttendee;
//bi-directional many-to-one association to EventOptionsAttendeeType
#ManyToOne
#JoinColumn(name="attendee_type_id", insertable=false, updatable=false)
private EventOptionsAttendeeType eventOptionsAttendeeType;
//bi-directional many-to-one association to EventProgram
#ManyToOne
#JoinColumn(name="program_id", insertable=false, updatable=false)
private EventProgram eventProgram;
public EventAttendeeLinkProgram() {
}
public EventAttendeeLinkProgramPK getId() {
return this.id;
}
public void setId(EventAttendeeLinkProgramPK id) {
this.id = id;
}
public EventAttendee getEventAttendee() {
return this.eventAttendee;
}
public void setEventAttendee(EventAttendee eventAttendee) {
this.eventAttendee = eventAttendee;
}
public EventOptionsAttendeeType getEventOptionsAttendeeType() {
return this.eventOptionsAttendeeType;
}
public void setEventOptionsAttendeeType(EventOptionsAttendeeType eventOptionsAttendeeType) {
this.eventOptionsAttendeeType = eventOptionsAttendeeType;
}
public EventProgram getEventProgram() {
return this.eventProgram;
}
public void setEventProgram(EventProgram eventProgram) {
this.eventProgram = eventProgram;
}
}
EventAttendeeLinkProgramPK.java
#Embeddable
public class EventAttendeeLinkProgramPK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#Column(name="attendee_id", insertable=false, updatable=false)
private int attendeeId;
#Column(name="attendee_type_id", insertable=false, updatable=false)
private int attendeeTypeId;
#Column(name="program_id", insertable=false, updatable=false)
private int programId;
public EventAttendeeLinkProgramPK() {
}
public int getAttendeeId() {
return this.attendeeId;
}
public void setAttendeeId(int attendeeId) {
this.attendeeId = attendeeId;
}
public int getAttendeeTypeId() {
return this.attendeeTypeId;
}
public void setAttendeeTypeId(int attendeeTypeId) {
this.attendeeTypeId = attendeeTypeId;
}
public int getProgramId() {
return this.programId;
}
public void setProgramId(int programId) {
this.programId = programId;
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof EventAttendeeLinkProgramPK)) {
return false;
}
EventAttendeeLinkProgramPK castOther = (EventAttendeeLinkProgramPK)other;
return
(this.attendeeId == castOther.attendeeId)
&& (this.attendeeTypeId == castOther.attendeeTypeId)
&& (this.programId == castOther.programId);
}
public int hashCode() {
final int prime = 31;
int hash = 17;
hash = hash * prime + this.attendeeId;
hash = hash * prime + this.attendeeTypeId;
hash = hash * prime + this.programId;
return hash;
}
}
EventAttendeeServiceImpl.java
#Service
#Primary
public class EventAttendeeServiceImpl implements EventAttendeeService {
#Autowired
private EventAttendeeRepository eventAttendeeRepository;
#Autowired
private EventOptionsAttendeeTypeRepository eventOptionsAttendeeTypeRepository;
#Autowired
private EventProgramRepository eventProgramRepository;
#Override
#Transactional
public String addEventAttendee(EventAttendee eventAttendee) {
EventAttendeeLinkProgram ep = new EventAttendeeLinkProgram();
ep.setEventOptionsAttendeeType(eventOptionsAttendeeTypeRepository.findOne(2L));
ep.setEventProgram(eventProgramRepository.findOne(2L));
eventAttendee.setEventAttendeeLinkPrograms(new ArrayList<>());
eventAttendee.getEventAttendeeLinkPrograms().add(ep);
eventAttendeeRepository.save(eventAttendee);
return "";
}
With this in place, my code is not throwing any errors. It is saving the EventAttendee, but nothing is being saved to the EventAttendeeLinkProgram. Please Note: I am trying so save both EventAttendee and EventAttendeeLinkProgram entities. So I think hibernate should be smart enought to forst save EventAttendee and generating the Id for it, then use that Id to store in EventAttendeeLinkProgram.
Why don't you let spring do the heavy lifting:
First create a JPA repository in spring:
public interface UserRepository extends CrudRepository<User, Long>{
}
Then create your 2 entities with the relationship
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true, fetch = FetchType.EAGER)
private List<UserType> userTypes;
And :
#Entity
public class UserType {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
My test looks like this:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
public class UserRepositoryTest extends AbstractTest {
#Autowired
private UserRepository userRepository;
#Test
#Transactional
public void test1() throws SQLException {
showTables();
User user1 = makeUser("Greg");
userRepository.save(user1);
System.out.println(user1);
userRepository.save(makeUser("George"));
assertEquals(2, userRepository.count());
User user = userRepository.findOne(1l);
}
User makeUser(String name) {
User user = new User();
user.setName(name);
user.setUserTypes(new ArrayList<>());
user.getUserTypes().add(makeUserType("admin"));
user.getUserTypes().add(makeUserType("head chef"));
return user;
}
UserType makeUserType(String description) {
UserType userType = new UserType();
userType.setDescription(description);
return userType;
}
}
First of all, user save return the identifier directly
Long insertId = (Long) session.save(user);
Then you'd better call the rollback on the txtransaction itself instead of retrieving again the transaction from the session.
Finally, when using spring you should consider to let spring manage the transaction itself (container managed transaction)using #Transactional annotation instead of using user managed transaction. It's logical as you let spring manage the session for you (sessionFactory.getCurrentSession()) and both session and transaction should have the same scope (e.g. the unit of work).
Consider reading some literature on Session (e.g. JPA entityManager) and transaction management.

Resources