8

I am in a situation where I want to get all table's column list using spring data jpa, my database is flexible so, the query should be work on all kind of database.

Mayur
  • 864
  • 6
  • 14
  • 25
  • I only use OpenJPA2.x where I can unwrap EM to an underlying jdbc connection (http://stackoverflow.com/questions/3493495/getting-database-connection-in-pure-jpa-setup). It's fine to use an oldschool jdbc functions on JPA application. JPA does not try to solve or abstract everything. – Whome Jul 06 '15 at 08:38

4 Answers4

10

JPA specification contains the Metamodel API that allows you to query information about the managed types and their managed fields. It does not however cover the underlying database. So, there is nothing out-of-the-box in JPA yet for querying the database metadata.


The way each RDBMS stores meta information is also different so there cannot be a simple, database-agnostic solution.


What you want can however be achieved through a few hops.

Step 1: Define an entity class that will hold metadata information.

@Entity
@IdClass(TableMetadataKey.class)
@Table(name = "table_metadata")
class TableMetadata {
  @Column(name = "column_name")
  @Id
  String columnName;

  @Column(name = "table_name")
  @Id
  String tableName;

  public static class TableMetadataKey implements Serializable {
    String columnName;
    String tableName;
  }
}

Step 2: Add the repository for the entity.

public interface TableMetadataRepository extends JpaRepository<TableMetadata, TableMetadataKey>
{
  TableMetadata findByTableName(String tableName);
}

Step 3: Define a database view named table_metadata to be mapped to the entity class. This will have to be defined using a database-specific query (because each database has a different way of storing its metadata).

Database-specific optimizations can be performed on this step, such as, using materialized views with Oracle for faster access, etc.

Alternatively, a table named table_metadata can be created with the required columns and populated periodically using a SQL script.

Now the application has full access to the required metadata.

List<TableMetadata> metadata = tableMetadataRepository.findAll()
TableMetadata metadata = tableMetadataRepository.findByTableName("myTable");

One issue to be noted is that not all tables in a schema may be mapped as JPA entities or not all columns in all tables may be mapped as entity fields. Therefore, directly querying the database metadata may give results that do not match the entity classes and fields.

manish
  • 19,695
  • 5
  • 67
  • 91
5

You can get the Column name list using your Entity or Model. What we need is @Column, which should be used in your Entity. You will get all the details which you have specified in @Column. All the parameters are Optional, although it is good to define all.

@Column(name, columnDefinition, insertable, length, nullable, precision, scale, table, unique, updatable)

We can get all fields declared in Entity by User.class.getDeclaredFields() ( in general ModelName.class.getDeclaredFields()). After getting all feilds we can get particular Column using field.getAnnotation(Column.class) we can also get all the details specified in @Column as below

Columns: @javax.persistence.Column(nullable=false, precision=2, unique=true, name=id, length=2, scale=1, updatable=false, columnDefinition=, table=, insertable=true)
Columns: @javax.persistence.Column(nullable=true, precision=0, unique=false, name=client_id, length=255, scale=0, updatable=true, columnDefinition=, table=, insertable=true)
Columns: @javax.persistence.Column(nullable=true, precision=0, unique=false, name=firstname, length=255, scale=0, updatable=true, columnDefinition=, table=, insertable=true)
Columns: @javax.persistence.Column(nullable=true, precision=0, unique=false, name=lastname, length=255, scale=0, updatable=true, columnDefinition=, table=, insertable=true)

create endPoint or method as per requirement

@GetMapping(value= "/columns/name")
    public List<String> tableColumnsName()
    {   
        List<String> Columns = new ArrayList<String>();
        Field[] fields = User.class.getDeclaredFields();

        for (Field field : fields) {
            Column col = field.getAnnotation(Column.class);
            if (col != null) {
                Columns.add(col.name());
                System.out.println("Columns: "+col);
            }
         }
          return Columns;   
    } 

Entity/Model

@Entity
@Table
public class User {

    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE)
    @Column(name="id")
    public int id;

    @Column(name="client_id")
    private int clientId;

    @Column(name="firstname")
    private String firstname;

    @Column(name="lastname")
    private String lastname;

    //AllArgConstructor-Constructor
    //Getters-Setters

}

Tested via Postman Postman Result

Romil Patel
  • 12,879
  • 7
  • 47
  • 76
2

SchemaCrawler has a Java API that allows you to work with database metadata in a general way, this is, without caring about the specific database platform.

http://www.schemacrawler.com

Marco
  • 1,377
  • 15
  • 18
0

The above solution works for "simple primary key". But for "composite primary key", the solution is mentioned below

BuidingKey --> Composite Primary key

@GetMapping("/columns")
public List<String> getColumns() {
    List<String> entityColumns = Arrays.asList(Building.class.getDeclaredFields()).stream().map(Field::getName)
            .collect(Collectors.toList());

    List<String> entityCompositePKColumns = Arrays.asList(BuidingKey.class.getDeclaredFields()).stream().map(Field::getName)
            .collect(Collectors.toList());

    entityCompositePKColumns.addAll(entityColumns);
    return entityCompositePKColumns;
}