Как я маплю сущности.
Базовые моменты, исключил частные случаи.
Не использовал аннотации валидации, решил что в dto их возможно лучше поставить.
Интересно узнать как делают в профессиональном коммерческом проекте?
Возможные ошибки? Фичи? Что то мб еще не знаю, и потому не использовал.
// указываю аннотации
@Entity // для всех jpa сущностей
@Table(name = "users", indexes = {
@Index(name = "idx_users_email", columnList = "email", unique = true)
}
) // опционально, по необходимости
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString(exclude = "accounts") // исключаю связные сущности, что бы избежать lazy exception
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class User extends BaseEntity {
// указываю что поле не должно быть пустым, unique я указал в @Table
@Column(nullable = false)
private String email;
@Column(nullable = false)
private String name;
// - сеттер делаю private для коллекций связных сущностей
// - каскад смотрим что нужно то и ставим, например я хочу что бы при удалении User
// удалялись и все аккаунты, то нужно CascadeType.REMOVE.
// - orphanRemoval - по необходимости, используем что бы в бд пошел запрос, если удаляешь в коде что то из коллекции
// - коллекцию инициализирую сразу, hibernate все равно подставит прокси когда надо, так что проблем не будет
@Setter(AccessLevel.PRIVATE)
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Account> accounts = new ArrayList<>();
// что бы не писать каждый раз в методах в джава коде,
// добавим методы для коллекций связных сущностей
public void addAccount(Account account) {
accounts.add(account);
account.setUser(this);
}
public void removeAccount(Account account) {
accounts.remove(account);
account.setUser(null);
}
}
@Entity
@Table(name = "accounts", indexes = {
@Index(name = "idx_account_account_number", columnList = "accountNumber", unique = true)
}
)
@AllArgsConstructor
// убираю возможность создать пустую сущность, не нужен аккаун у которого user = null
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Setter
@Getter
@ToString(exclude = "user")
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Account extends BaseEntity {
@Column(name = "account_number", nullable = false)
private String accountNumber;
private BigDecimal balance;
@ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
@JoinColumn(name = "user_id") // маппинг на стороне чаилда, поэтому foreign key указываем в accounts
private User user;
}
// добавляем base class что бы вынести совподающие поля потом сделать extends
// таблицы в бд для этого класса не будет
@MappedSuperclass
@Setter
@Getter
public class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
- аннотации валидации не ставил, решил что возможно их лучше в dto проставить.
- не все параметры использовал, чисто основные моменты.
- не использую @Data - как будто поведение становится менее контролируемым.
- осторожно с @ToString и @EqualsAndHashCode, могут возникнуть проблемы с связными сущностями.
SQL скрипты такие:
(возможно что то не учел, или сделал ошибку, т.к. специально не разбирал, просто с головы как понял)
create table users (
id bigint not null auto_increment,
email varchar(50) not null unique,
name varchar(25) not null,
constraint users_pk primary key (id),
constraint users_email_ak unique (email)
)
create table accounts (
id bigint not null auto_increment,
user_id bigint not null,
account_number varchar(150) not null unique,
balance decimal(6,1),
constraint accounts_pk primary key (id),
constraint accounts_users_fk foreign key (user_id) references users(id) on delete cascade
)
--для индекса например так:
create unique index idx_account_account_number on accounts(account_number);
Так же интересно часто ли джуны с бд работают на прямую, грубо говоря, приходится ли писать sql, например по созданию таблиц.