Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get Column names of JPA entity

All my JPA entity classes implement an interface called Entity which is defined like this:

public interface Entity extends Serializable {
// some methods

}

Some of the fields of my JPA entity have @Column annotation on top of them and some don't. MyEntity class is defined like below:

@Entity
public class MyEntity implements Entity {

   @Id
   private Long id; // Assume that it is auto-generated using a sequence. 

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


   private SecureString field2; //SecureString is a custom class

   //getters and setters

}

My delete method accepts an Entity.

@Override
public void delete(Entity baseEntity) {

   em.remove(baseEntity); //em is entityManager
}

Whenever the delete method is invoked I want three things inside my delete method:

1) Fields of MyEntity that are of type SecureString

2) Column name of that particular field in DB (The field may or may not have @Column annotation)

3) The value of id field

Note that when the delete() method is invoked, we don't know for which entity it is invoked, it may be for MyEntity1, MyEntity2 etc.

I have tried doing something like below:

for (Field field : baseEntity.getClass().getFields()) {
    if (SecureString.class.isAssignableFrom(field.getType())) {
        // But the field doesn't have annotation @Column specified
        Column column = field.getAnnotation(Column.class);

        String columnName = column.name();
    }
}

But this will only work if the field has @Column annotation. Also it doesn't get me other two things that I need. Any ideas?

like image 998
Aman Avatar asked Sep 21 '17 04:09

Aman


1 Answers

Hibernate can use different naming strategies to map property names, which are defined implicitly (without @Column(name = "...")). To have a 'physical' names you need to dive into Hibernate internals. First, you have to wire an EntityManagerFactory to your service.

@Autowired
private EntityManagerFactory entityManagerFactory;

Second, you have to retrieve an AbstractEntityPersister for your class

    SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
    AbstractEntityPersister persister = ((AbstractEntityPersister)sessionFactory.getClassMetadata(baseEntity.getClass()));

Third, you're almost there with your code. You just have to handle both cases - with and without @Column annotation. Try this:

for (Field field : baseEntity.getClass().getFields()) {
    if (SecureString.class.isAssignableFrom(field.getType())) {
        String columnName;
        if (field.isAnnotationPresent(Column.class)) {
            columnName = field.getAnnotation(Column.class).name();
        } else {
            String[] columnNames = persister.getPropertyColumnNames(field.getName());
            if (columnNames.length > 0) {
                columnName = columnNames[0];
            }
        }
    }
}

Note that getPropertyColumnNames() retrieves only 'property' fields, that are not a part of primary key. To retrieve key column names, use getKeyColumnNames().

And about id field. Do you really need to have all @Id's in child classes? Maybe would better to move @Id to Entity class and mark this class with @MappedSuperclass annotation? Then you can retrieve it just with baseEntity.getId();

like image 74
Anatoly Shamov Avatar answered Nov 19 '22 22:11

Anatoly Shamov