Entities

The basic building block of FeGen's and Spring's data model are entities. Those are classes annotated with javax.persistence.Entity for which a database table will be created where instances of those classes can be stored together with their fields. For an entity class to work with FeGen, it needs to meet the following criteria:

  • They must be located in the package specified by scanPkg
  • They must have an @Entity annotation
  • They have an id field of type long
    • That field is annotated with @Id and @GeneratedValue
  • Other fields are annotated with a @Column annotation specifying nullable as true or false (even in Kotlin)
    • @Notnull and @Nullable from Spring validation may be used instead
  • They have a base projection

The last one is important since there are certain scenarios in which entities returned by the backend will not have certain properties that are required by the code generated by FeGen.

Base projection

A base projection is necessary for an entity to be correctly returned when using FeGen to request it in a client. It is an interface definition that is nested within the entity class and meets the following criteria:

  • It is annotated with @Projection
  • The name property of its @Projection annotation must be "baseProjection"
  • The types property of the annotation contains only the class of the entity for which the interface is a projection
  • It must include a getter for all fields whose types are not entities nor projections including the id field.

Java

An example entity in Java may look like this:

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

import org.springframework.data.rest.core.config.Projection;
import org.springframework.lang.Nullable;

@Entity
public class SomeEntity {

    @Id
    @GeneratedValue
    public long id;

    @Column(nullable = false)
    public String nonNullableField;

    @Nullable
    public String nullableField;

    private long value;

    public long getValue() {
        return value;
    }

    public void setValue(long value) {
        this.value = value;
    }

    private boolean b;

    public boolean isB() {
        return b;
    }

    public void setB(boolean b) {
        this.b = b;
    }

    @Projection(name = "baseProjection", types = {SomeEntity.class})
    interface BaseProjection {
        long getId();
        String getNonNullableField();
        String getNullableField();
        long getValue();
        boolean isB();
    }
}

As you can see, there are two options of defining entity columns. You can either just use a single field or specify a getter for your field (like it was done for the value and b columns). Since Spring and FeGen can conclude from the name and signature of the function that getValue is a getter for value, only one column named value is created. If you wanted to annotate the value or b column, you could do so on the getter or the field.

Since the value and b columns have primitive types, you do not need to specify their nullability.

Kotlin

The entity from the Java example may look like this if written in Kotlin:

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.springframework.lang.Nullable;

@Entity
class SomeEntity {
    
    @Id
    @GeneratedValue
    var id: Long = -1

    @Column(nullable = false)
    lateinit var nonNullableField: String
    
    @Nullable
    var nullableField: String? = null
    
    var value: Long = 0
    
    var b: Boolean = false

    @Projection(name = "baseProjection", types = [SomeEntity::class])
    interface BaseProjection {
        val id: Long
        val firstName: String
        val lastName: String
        val number: String?
    }
}

In Kotlin, it is best to define columns using the var keyword. If the type of your column is primitive, you need to initialize it like it is the case with id and value. Otherwise, you can use the lateinit modifier to avoid having to initialize a property that will often be overwritten by the contents of the database anyway.

You still have to specify nullability for non-primitive properties, even though Kotlin has a notion of nullability in its types, because Spring will ignore them.

For the base projection you can just use vals as they will be recognized by Spring in the same way as getters are.

Repositories

Without a repository, you will not be able to perform CRUD operations on your entity from the code generated by FeGen.

The repository for your entity must be an interface that meets the following criteria:

  • It is located in the package specified by scanPkg
  • It extends JpaRepository with your entity and Long as type parameters

In Java, it will look like this:

public interface SomeEntityRepository extends JpaRepository<SomeEntity, Long> {}

In Kotlin, it will look like this:

interface SomeEntityRepository: JpaRepository<SomeEntity, Long>

Frontend

To see how you can access your entities from your frontend, take a look at the Typescript or Kotlin page depending on what client language you are generating code for.