Spring boot JPARepository doesn't execute save - spring-boot

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;
}
}

Related

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();
}
}

How to combine two DAOs in generic way with only one method in Service layer in Spring Boot

Is it possible to combine two DAOs into one Service method?
I want to create a generic method which will choose correct DAO based on the input parameter. What for now I came up with is the method which will accept Dao from the outside the service object. But this requires to initialize appropriate Dao in the Controller which is a little bit ugly...
Measurement is just an interface for Temperature.java and Humidity.java entities with separate tables on PostgreSQL.
#Service
public class MeasurementService {
#Autowired
private TemperatureDao temperatureDao;
#Autowired
private HumidityDao humidityDao;
public<T extends PagingAndSortingRepository<Measurement, Long>> void insertMeasurementForUser(String username, List<Measurement> measurements, T dao) {
dao.saveAll(measurements);
}
}
TemperatureDao.java
#Repository
public interface TemperatureDao extends PagingAndSortingRepository<Temperature, Long> {
#Query("select u from Temperature u where u.owner = ?1 order by u.id desc")
List<Temperature> findLatestTemperatureForUser(User user, Pageable pageable);
}
HumidityDao.java
#Repository
public interface HumidityDao extends PagingAndSortingRepository<Humidity, Long> {
#Query("select u from Humidity u where u.owner = ?1 order by u.id desc")
List<Humidity> findLatestHumidityForUser(User user, Pageable pageable);
}
Temperature.java
#Entity
#Table(name = "temperature")
public class Temperature implements Measurement {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#Column(name = "th1value")
private Float th1Value;
#Column(name = "timestamp")
#NotNull
private LocalDateTime timestamp;
#ManyToOne
#JoinColumn(name = "user_id")
#NotNull
private User owner;
public Temperature() {
}
public Temperature(Float th1Value, LocalDateTime timestamp, User owner) {
this.th1Value = th1Value;
this.timestamp = timestamp;
this.owner = owner;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
public LocalDateTime getTimestamp() {
return timestamp;
}
#JsonSerialize(using = LocalDateTimeSerializer.class)
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
#Override
public User getOwner() {
return owner;
}
#Override
public void setOwner(User owner) {
this.owner = owner;
}
}
Humidity.java
#Entity
#Table(name = "humidity")
public class Humidity {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#Column(name = "hum1value")
private Float hum1Value;
#Column(name = "timestamp")
#NotNull
private LocalDateTime timestamp;
#ManyToOne
#JoinColumn(name = "user_id")
#NotNull
private User owner;
public Humidity() {
}
public Humidity(Float hum1Value, LocalDateTime timestamp, User owner) {
this.hum1Value = hum1Value;
this.timestamp = timestamp;
this.owner = owner;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
public LocalDateTime getTimestamp() {
return timestamp;
}
#JsonSerialize(using = LocalDateTimeSerializer.class)
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
public User getOwner() {
return owner;
}
public void setOwner(User owner) {
this.owner = owner;
}
}
Any ideas?
You could write a Resolver pattern to return needed dao based on your conditions. You service will use the resolver to get the correct dao.
public HellDao implements BaseDao {
public void save();
}
public ByeDao implements BaseDao {
public void save();
}
public DaoResolver {
#Autowired
private helloDao;
#Autowired
private byeDao;
public BaseDao resolve(Object input) {
//based on input return the correct dao
BaseDao resolvedDao = null;
switch(input.enum) {
case Hello:
resolvedDao = helloDao;
break;
case Hello:
resolvedDao = byeDao;
break;
default:
//decide something for default
}
return resolvedDao;
}
}
public class MyService {
#Autowired
private DaoResolver daoResolver;
public Object doSomething() {
BaseDao dao = daoResolver.resolve(someObject);
//you will get HelloDao or ByeDao based on the input
dao.save();
}
}
You can check for the type of measurements using instanceof so you could do it without generics.
public void insertMeasurementForUser(String username, List<Measurement> measurements) {
if(measurements.get(0) instanceof Temperature)
temperatureDao.saveAll(measurements);
else if(measurements.get(0) instanceof Humidity)
humidityDao.saveAll(measurements);
}

Getting ConstraintViolationException while saving a row with embedded key in the table with many-to-many mapping between two entities using Spring JPA

In our spring boot Restful WebService, we have two master tables with many-to-many relationship between them. But in the transaction table, we want one extra field (current_time) as part of the embedded key other than the primary keys of the two tables. Now, we’ve created a separate class for defining embedded primary key using #Embeddable. Now, while inserting one transaction row to transaction table using Spring JPA, I am manually setting the primary keys in the corresponding entity and calling the save method on corresponding repository. But It is giving me ConstraintViolationException as the current_time is going with null value even if I have manually set it. Any help would be highly appreciated.
First Entity is as follows :
#Entity
#Table(name = "project")
public class Project {
#Id
#GenericGenerator(name = "projectid", strategy = "com.sample.upload.entity.ProjectIDGenerator")
#GeneratedValue(generator = "projectid")
#Column(name = "projectid")
private String projectID;
#Column(name = "project_name")
private String projectName;
#Column(name = "project_descr")
private String projectDesc;
#Column(name = "project_input_path")
private String projectPath;
#Column(name = "project_creation_time")
private Calendar projectCreationTime;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "project_migration", joinColumns = #JoinColumn(name = "projectid", referencedColumnName = "projectid"), inverseJoinColumns = #JoinColumn(name = "migratorid", referencedColumnName = "migratorid"))
private List<Migrator> migrators;
#Column(name = "account_name")
private String accountName;
#Column(name = "account_group")
private String accountGroup;
public String getProjectID() {
return projectID;
}
public void setProjectID(String projectID) {
this.projectID = projectID;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public String getAccountGroup() {
return accountGroup;
}
public void setAccountGroup(String accountGroup) {
this.accountGroup = accountGroup;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getProjectDesc() {
return projectDesc;
}
public void setProjectDesc(String projectDesc) {
this.projectDesc = projectDesc;
}
public String getProjectPath() {
return projectPath;
}
public void setProjectPath(String projectPath) {
this.projectPath = projectPath;
}
public Calendar getProjectCreationTime() {
return projectCreationTime;
}
public void setProjectCreationTime(Calendar projectCreationTime) {
this.projectCreationTime = projectCreationTime;
}
public List<Migrator> getMigrators() {
return migrators;
}
public void setMigrators(List<Migrator> migrators) {
this.migrators = migrators;
}
}
Second Entity :
#Entity
#GenericGenerator(name = "generatorName", strategy = "increment")
#Table(name = "migrator")
public class Migrator {
#Id
#GeneratedValue(generator = "generatorName")
#Column(name = "migratorid")
private String migratorId;
#Column(name = "src_tech_name")
private String srcTechName;
#Column(name = "dest_tech_name")
private String destTechName;
#Column(name = "migrator_name")
private String migratorName;
#Column(name = "migrator_type")
private String migratorType;
public String getMigratorId() {
return migratorId;
}
public void setMigratorId(String migratorId) {
this.migratorId = migratorId;
}
public String getSrcTechName() {
return srcTechName;
}
public void setSrcTechName(String srcTechName) {
this.srcTechName = srcTechName;
}
public String getDestTechName() {
return destTechName;
}
public void setDestTechName(String destTechName) {
this.destTechName = destTechName;
}
public String getMigratorName() {
return migratorName;
}
public void setMigratorName(String migratorName) {
this.migratorName = migratorName;
}
public String getMigratorType() {
return migratorType;
}
public void setMigratorType(String migratorType) {
this.migratorType = migratorType;
}
#Override
public String toString() {
return "Technology [migratorId=" + migratorId + ", srcTechName=" + srcTechName + ", destTechName="
+ destTechName + ", migratorName=" + migratorName + ", migratorType=" + migratorType + "]";
}
}
The join (transaction) table's entity :
#Entity
#Table(name = "project_migration")
public class ProjectMigration {
#EmbeddedId
private ProjectMigrationID migrationId;
#Column(name ="migration_finish_time")
private Calendar migrationFinishTime;
#Column(name ="time_in_millis_for_migration")
private long timeInMillisForMigration;
#Column(name ="migration_status")
private String migrationStatus;
#Column(name ="migrated_codebase_path")
private String migratedCodeBasePath;
The embedded Primary Key class is as follows:
#Embeddable
public class ProjectMigrationID implements Serializable {
private static final long serialVersionUID = -3623993529011381924L;
#Column(name = "projectid")
private String projectId;
#Column(name = "migratorid")
private String migratorId;
#Column(name = "migration_start_time")
private Calendar migrationStartTime;
public ProjectMigrationID() {
}
public ProjectMigrationID(String projectId, String migratorId, Calendar migrationStartTime) {
this.projectId = projectId;
this.migratorId = migratorId;
this.migrationStartTime = migrationStartTime;
}
The snippet from service Class :
for (String migratorId : data.getMigratorIds()) {
Migrator migrator = migratorRepository.findByMigratorId(migratorId);
migrators.add(migrator);
}
if (projectId != null) {
project = projectRepository.findByProjectID(projectId);
System.out.println(project==null);
project.setMigrators(migrators);
System.out.println("I am here");
if (project != null) {
//project.setMigrationStatus("In Progress");
ProjectMigrationID pmId = new ProjectMigrationID();
pmId.setProjectId(project.getProjectID());
pmId.setMigratorId(project.getMigrators().get(0).getMigratorId());
pmId.setMigrationStartTime(new GregorianCalendar());
ProjectMigration pm = new ProjectMigration();
pm.setMigrationId(pmId);
pm.setMigrationStatus("Pending");
projectMigrationRepository.save(pm);
That's because of the #JoinTable where the date is not included and it skips the insertion. If you include a column with all the primary keys needed, it will work as expected.
Only the columns mapped via #JoinTable will be included during insertion or update (defaults to true when mapped)
Either include the date time column in the Project class or use association without #JoinTable.
I'm editing via mobile. So please ignore typos if any.

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.

Spring JPA repository query can't find ID property

I need a spring repository method that lets me get a list of Scene entities using a list of id's. When I try to refer to the Scene Id I get an error, saying it can't find the property called IdScene. I am using a custom query to do this. Is there something wrong with my query?
My Entity is
public class Scene implements Serializable
{
private long id_scene;
private Media media;
private int sceneNumber;
private int version;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id_scene")
public long getIdScene() {
return id_scene;
}
public void setIdScene(long id_scene) {
this.id_scene = id_scene;
}
#ManyToOne
#JoinColumn(name = "id_media")
public Media getMedia() {
return this.media;
}
public void setMedia(Media media) {
this.media = media;
}
private List<Thumbnail> thumbnails = new ArrayList<Thumbnail>();
#OneToMany(mappedBy = "scene", cascade=CascadeType.ALL,
orphanRemoval=true)
#LazyCollection(LazyCollectionOption.FALSE)
public List<Thumbnail> getThumbnails() {
return this.thumbnails;
}
public void setThumbnails(List<Thumbnail> thumbnails) {
this.thumbnails = thumbnails;
}
public void addThumbnail(Thumbnail thumbnail) {
thumbnail.setScene(this);
this.thumbnails.add(thumbnail);
}
private Property property;
#OneToOne(mappedBy="scene", cascade=CascadeType.ALL,
orphanRemoval=true)
#LazyCollection(LazyCollectionOption.FALSE)
public Property getProperty() {
return property;
}
public void setProperty(Property property) {
this.property = property;
}
public void addProperty(Property property) {
property.setScene(this);
this.property = property;
}
#Column(name = "sceneNumber")
public int getSceneNumber() {
return sceneNumber;
}
public void setSceneNumber(int sceneNumber) {
this.sceneNumber = sceneNumber;
}
#Column(name = "version")
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
}
My repository:
public interface SceneRepository extends JpaRepository<Scene, Long> {
public final static String FIND_BY_ID_LIST = "SELECT s"
+ " FROM Scene s WHERE s.IdScene IN (:id)";
#Query(FIND_BY_ID_LIST)
public List<Scene> findByIdScene(#Param("id") List<Long> id);//, Pageable page);
}
try:
"SELECT s FROM Scene s WHERE s.idScene IN (:id)"
note lower case 'i' in 'idScene'"
This is because of the Java Bean naming convention, a property defined as:
public String getWibble() { return wibble; }
public void setWibble(String value) { wibble = value; }
defines wibble and not Wibble

Resources