JPA Buddy allows you to granularly pick tables/views and fields from your database and get them as JPA entities:
In the IntelliJ IDEA Community Edition, you can generate entities from DB via:
In the IntelliJ IDEA Ultimate Edition, you can generate entities from DB via:
The menu on the top of the window allows you to configure:
@Table
annotationAlso, from the "Other settings" drop-down list, you can move to the entity declaration and reverse engineering settings.
On the left side of the window, you can see:
After selecting any element from the tree, a panel for migrating attributes from columns will appear. Also, you will be able to define a class name in the corresponding field.
The main part of the window allows you to configure everything related to attributes. You can choose which attributes you want to add and change all their params, except "Column Name". Mapping type and attribute/converter/hibernate types are represented as drop-down lists.
All attributes are divided into 3 categories:
For attributes matching the String
or Integer
type, you can change the mapping type from Basic to Enum, and JPA Buddy will create the corresponding Enum class in the project. You need to fill the enum with proper values manually.
For some SQL types, there is no exact match to Java classes. In this case, JPA Buddy does not set the type so as not to generate non-working code. You will need to choose the attribute type yourself. You can also configure default type mappings for each DBMS in the settings.
If you have the HibernateTypes library in your project dependencies list, JPA Buddy can find suitable types in this library and automatically suggests them for the unsupported SQL types during reverse engineering:
If you want to postpone an attribute creation for specific columns, you can choose //todo comment
as the mapping type. JPA Buddy will generate the //todo comment with the corresponding quick-fix actions depending on the column type. You can call these actions via ⌘+B (Ctrl+B) shortcut:
Here is the example of generated //todo comment for the attribute with unknown column type:
/*
TODO [JPA Buddy] create field to map the 'description' column
Available actions: Define target Java type | Uncomment as is | Remove column mapping
@Column(name = "description", columnDefinition = "jsonb")
private java.lang.Object description;
*/
After calling the "Define target Java type" action, the following window will appear:
JPA Buddy will remember data mappings for the subsequent reverse engineering actions. You can always change them in the settings.
JPA Buddy follows all best practices providing the most efficient mapping for DB views while reverse engineering:
@Immutable
annotation to the entity and generates getters only. This helps to achieve better application performance.Some developers prefer the DB-first application development approach. First, they add columns directly to the database and then update the JPA model. JPA Buddy can automate this process. To add attributes to the existing entity, choose From DB action in JPA Designer (1), Editor Toolbar (2) or from the IntelliJ IDEA "Generate" menu (3):
After that, the Reverse Engineering Columns wizard will appear:
The attributes migration flow here is identical to what was described in the Entities from DB wizard section.
JPA Buddy deeply understands your model. In certain cases, it's able to properly detect cardinality: @
OneToOne, @
OneToMany, @
ManyToOne, @
ManyToMany. The coolest thing is that JPA Buddy can show references for which there are no columns in the current table.
Let's look more closely at each of these cases.
There are two situations when we know for 100% that the cardinality of the relation is exactly @
OneToOne:
Case №1:
CREATE TABLE profiles
(
id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
join_date date,
user_id BIGINT,
status VARCHAR(255),
bio VARCHAR(255),
CONSTRAINT pk_profiles PRIMARY KEY (id)
);
CREATE TABLE users
(
id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
last_name VARCHAR(255),
first_name VARCHAR(255),
CONSTRAINT pk_users PRIMARY KEY (id)
);
ALTER TABLE profiles
ADD CONSTRAINT uc_profiles_user UNIQUE (user_id);
ALTER TABLE profiles
ADD CONSTRAINT FK_PROFILES_ON_USER FOREIGN KEY (user_id) REFERENCES users (id);
JPA Buddy will generate @
OneToOne association with @
JoinColumn annotation in the User entity, and @
OneToOne association with mappedBy
parameter in the Profile entity:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "profile_id")
private Profile profile;
}
@Entity
@Table(name = "profiles")
public class Profile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@OneToOne(fetch = FetchType.LAZY, mappedBy = "profile")
private User users;
}
Case №2:
CREATE TABLE users
(
id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
last_name VARCHAR(255),
first_name VARCHAR(255),
CONSTRAINT pk_users PRIMARY KEY (id)
);
CREATE TABLE profiles
(
user_id BIGINT NOT NULL,
status VARCHAR(255),
bio VARCHAR(255),
join_date date,
CONSTRAINT pk_profiles PRIMARY KEY (user_id)
);
ALTER TABLE profiles
ADD CONSTRAINT FK_PROFILES_ON_USER FOREIGN KEY (user_id) REFERENCES users (id);
Since @
Id should not be a persistence entity, JPA Buddy will generate:
id
attribute of basic type and mark it with @
Id annotationusers
@
OneToOne association and mark it with @
MapsId annotation@Entity
@Table(name = "profiles")
public class Profile {
@Id
@Column(name = "user_id", nullable = false)
private Long id;
@MapsId
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", nullable = false)
private User users;
//...
}
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@OneToOne(fetch = FetchType.LAZY, mappedBy = "user")
private Profile profiles;
//...
}
If a table has the column that refers to the primary key of another table, it is highly likely @
ManyToOne association. But you are also able to change cardinality to @
OneToOne if required. So, depending on which table you call the reverse engineering action, JPA Buddy will detect mapping type as @
OneToMany or @
ManyToOne:
CREATE TABLE users
(
id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
last_name VARCHAR(255),
first_name VARCHAR(255),
CONSTRAINT pk_users PRIMARY KEY (id)
);
CREATE TABLE profiles
(
id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
join_date date,
status VARCHAR(255),
bio VARCHAR(255),
user_id BIGINT,
CONSTRAINT pk_profiles PRIMARY KEY (id)
);
ALTER TABLE profiles
ADD CONSTRAINT FK_PROFILES_ON_USER FOREIGN KEY (user_id) REFERENCES users (id);
JPA Buddy will generate the following code:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@OneToMany(mappedBy = "user")
private Set<Profile> profiles = new LinkedHashSet<>();
//...
}
@Entity
@Table(name = "profiles")
public class Profile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
//...
}
To establish a many-to-many relationship between two tables, you need to use a junction table. The junction table, in this case, contains only two columns - foreign keys. Since JPA Buddy found such a table, it can say that the relation cardinality between two tables whose ids are represented in the junction table as foreign keys is @
ManyToMany.
CREATE TABLE users
(
id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
last_name VARCHAR(255),
first_name VARCHAR(255),
CONSTRAINT pk_users PRIMARY KEY (id)
);
CREATE TABLE profiles
(
id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
join_date date,
status VARCHAR(255),
bio VARCHAR(255),
CONSTRAINT pk_profiles PRIMARY KEY (id)
);
CREATE TABLE profiles_users
(
profile_id BIGINT NOT NULL,
users_id BIGINT NOT NULL,
CONSTRAINT pk_profiles_users PRIMARY KEY (profile_id, users_id)
);
ALTER TABLE profiles_users
ADD CONSTRAINT fk_prouse_on_profile FOREIGN KEY (profile_id) REFERENCES profiles (id);
ALTER TABLE profiles_users
ADD CONSTRAINT fk_prouse_on_user FOREIGN KEY (users_id) REFERENCES users (id);
If this association does not exist in any of the entities, JPA Buddy will generate it in the entity for which the reverse engineering action was called.
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@ManyToMany
@JoinTable(name = "profiles_users",
joinColumns = @JoinColumn(name = "users_id"),
inverseJoinColumns = @JoinColumn(name = "profile_id"))
private Set<Profile> profiles = new LinkedHashSet<>();
//...
}
If this association already exists in one of the entities, then JPA Buddy will generate the @
ManyToMany attribute with the mappedBy
parameter.
@Entity
@Table(name = "profiles")
public class Profile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@ManyToMany(mappedBy = "profiles")
private Set<User> users = new LinkedHashSet<>();
//...
}
The larger the database and the slower the connection of the database (for example, if it is remote DB), the longer it will take to load DB schema. For better usability, JPA Buddy provides a DB schema cache. Once you enable it (1), a snapshot file will be created for the selected DB in the temporary directory. Otherwise, the DB schema will be loaded from the DB on each reverse engineering use. When you need it, you can refresh saved schema cache (2).
FetchType.LAZY
for @OneToOne
and @ManyToOne
associations by default.users
, JPA Buddy will generate a User
entity. If you disable this option, JPA Buddy will keep the original name of the table and only capitalize the first letter – Users
.Often, DBA specialists adhere to certain naming conventions for database objects. For example, all table or column names have a specific prefix. Yet, Java developers usually prefer to drop these prefixes for the JPA model. JPA Buddy allows you to specify prefixes to skip. Assume we set sys_
and p_
as prefixes to skip. After that, we apply reverse engineering for sys_user
and p_product
tables. As a result, prefixes will not appear in the corresponding entity names. The final entity names will be User
and Product
instead of SysUser
and PProduct
.
Also, the database column names sometimes match the reserved Java keywords. E.g., public
, interface
, and so on... In this case, you can configure the field suffix so that JPA Buddy will append it to the original column name. E.g. for the Field
suffix, the resulting names will be publicField
and interfaceField
.
When the application works with several DBMSs, your schema might have slightly different data types for each of them.
Let's say the application needs to support both PostgreSQL and MS SQL and you need to store Unicode characters in string data. PostgreSQL supports Unicode chars in VARCHAR
, but MS SQL has a separate NVARCHAR
data type for it.
JPA Buddy lets you specify type mappings for each DBMS. It is also possible to set mappings for JPA Converters and Hibernate Types:
See how you can configure type mappings for reverse engineering in JPA Buddy to make use of the @JavaType
annotation from Hibernate 6: