Reflection: Suche Feld + in Unterklassen

internet

Top Contributor
Hallo,

ich suche eine Möglichkeit beginnend von einer Klasse die Felder nach einem bestimmten Feldname zu suchen.
Ebenfalls sollen die Relationen innerhalb der Klasse berücksichtigt werden.

Also bspw. suche ich nach einem Feld "username":
Wenn ich von der Klasse "Employee" starte, soll eben auch alle Referenzen berücksichtigt werden. Also ebenfalls "User". Dann soll in der Klasse "User" nach "username" gesucht werden.

Wenn ich diese Klasse habe:
Java:
public class Employee {
    private User user;
}

public class User {
    private String username;
}

Wie kann ich das machen? Speziell den Aufruf und Berücksichtigung der "Referenzen".
Wie ich das Feld suchen kann, ist mir durchaus klar:

Code:
Field field = myobject.getClass().getDeclaredField("username");
field.setAccessible(true);
String databaseField = (String) field.get(myobject);
 
Wo ist denn das genaue Problem?

Du hast doch eigentlich schon alles, was Du brauchst.

Du kannst bei einer Klasse alle Felder (getFields()) holen und diese durchgehen.

Also wenn Du ein Employee hast, dann kannst Du dir alle Felder holen und diese dann durchgehen:
  • gibt es einen "username" -> fertig
  • Wenn es keinen username gab, dann gehst Du die Felder alle durch und holst Dir den Inhalt.
-- Ist der Inhalt null? Dann gehe weiter
-- sonst-> Rekursive Prüfung aufrufen. Hat das ein Ergebnis ergeben? -> fertig

Hier musst Du aber halt die Anforderungen prüfen und gewisse Spezialfälle beachten:
  • Zirkuläre Referenzen / Klassen. Employee hat evtl. einen Boss und der hat auch einen username? Etwas in der Art. Oder ein Feld reportsTo, das auf den Employee selbst zeigt ...
  • Collections und Array wollen evtl. auch speziell behandelt werden
  • ...
 
irgendwie gehen seit geraumer Zeit bei mir die Benachrichtigung im Forum nicht mehr, aber ein anderes Thema..

Wo ist denn das genaue Problem?

Der Aufruf bzw. Durchgehen beim Feld "User" ist dann aktuell das Problem...
Also hier brauche ich ja irgendwie eine Bedingung, die nicht nur einen Integer, String etc. prüft, sondern wiederum von dieser Klasse..
 
Den Ablauf hatte ich doch beschrieben. Ich verstehe daher weiter nicht das Problem, das Du hast.

Also einfach mal umgesetzt, wie sowas auf die Schnelle aussehen könnte - ohne jetzt die angesprochen Fälle irgendwie zu berücksichtigen. Und ich habe die Zugriffsrechte auf die Felder sowie das Behandeln der Exception außen vor gelassen.

Java:
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Optional;

public final class ReflectionTest {

    public static class Employee {
        public User user;
    }

    public static class User {
        public String username;
    }

    public static String findUsername(Object obj) throws IllegalAccessException {
        Field[] fields = obj.getClass().getFields();

        Optional<Field> usernameField = Arrays.stream(fields)
                .filter( f -> "username".equals(f.getName()))
                .findAny();

        if (usernameField.isPresent())
            return (String) usernameField.get().get(obj);

        for (Field field : fields) {
            Object fieldContent = field.get(obj);
            if (fieldContent != null) {
                String result = findUsername(fieldContent);
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }

    public static void main(final String[] args) throws IllegalAccessException {

        Employee employee = new Employee();
        System.out.println("On empty employee: " + findUsername(employee));

        employee.user = new User();
        employee.user.username = "JohnDoe";

        System.out.println("On filled employee: " + findUsername(employee));
    }
}
 
danke.
Aber wie kann ich auch noch die ganzen vererbten Felder berücksichtigen? Das scheint es aktuell nicht?

Java:
public class User extends UserImpl {
....
}

Im Prinzip möchte ich mir mein SQL (JPA) damit zusammen bauen und suche nach einem bestimmten Feld.
 
Ich würde Dir raten, doch einmal genauer in die Klassen, die Du benutzt zu schauen.

So gibt es die Methode getSuperclass() - Du kannst also damit also auch die Vererbungshirarchie durchgehen.

Generell ist die Frage aber dennoch, was Du da genau aufbauen willst. Was für ein SQL willst Du genau aufbauen? Im Augenblick klingt das alles etwas dubios. Zumal Du ja nur die Java Klassen durchgehst und Du in SQL ja die Datenbank Feldnamen brauchst und die können abweichen...
 
ok, dann schweife ich mal näher aus.
Ich habe eine Webapp, das mandantenfähig ist. Demnach habe ich verschiedene Entitäten, die meistens so aufgebaut sind:
CustomerImpl verwende ich aktuell für zwei Entities a) Der Customer selbst b) Eine Log - Tabelle. Daher die Vererbung.

Java:
@Entity
@Table(name = "customer")
public class Customer extends CustomerImpl  {
    
    @Id
    @GeneratedValue(generator = "customer_id_seq", strategy = AUTO)
    @SequenceGenerator(name = "customer_id_seq", sequenceName = "customer_id_seq", allocationSize = 1)
    @Column(name = "customer_id", nullable = false)
    private Long id = NO_ID;
}


@MappedSuperclass
public class CustomerImpl implements Serializable, EntityPropertyFinder, Cloneable {

    // Emailadresse
    @Size(max = Constants.SIZE_MAX_STRING)
    @Column(name = "email", length = Constants.SIZE_MAX_STRING)
    private String email = null;

}

Nun habe ich die verschiedene Entities, wie Invoice, Offer etc.
Ausgenommen Entities für die App selbst (Adminzeugs) haben die irgendwo eine Abhängig zum User.
Meistens eine direkte Verbindung, also

Code:
@ManyToOne(optional = false)
@NotNull
@JoinColumn(name = "user_fk")
 private User user;

Manchmal muss man eben über mehrere Entities gehen, damit man den User bekommt.
Also zB bei InvoicePosition:
-> InvoicePosition -> Invoice -> User

Nun will ich gerne ein SQL bzw. JPA dynamisch aufbauen um auf den User filtern zu können.
"Username" war oben der Einfachheit genannt, prinzipiell brauche ich vom Objekt "User" das Feld "id" um dann im SQL auf den aktuell User, den ich über einen Parameter filtern kann.

Allerdings reicht auch einfach nur der Pfad zum User - Objekt, also bspw. invoice.user...

+ ".id" im String kann ich auch selbst hinzufügen und ich bin flexibler, falls ich auch mal ein anderes Feld vom User brauche.

Hoffe das war verständlich 🙂
 
Du kannst es so machen - sinnvoll ist es aber nicht.

Wenn man an solche Probleme mit Reflection rangeht, investiert man in der Regel zu viel Zeit und hat am Ende nicht mehr wartbaren und verständlichen Code. Insbesondere weil man zig Sonderfälle (Collections, Zirkuläre Abhängigkeiten) berücksichtigen muss. Performant ist das auch nicht mehr, weil vermutlich jedes Objekt eine mittlere 2 bis 3 stellige Zahl an verlinkten Objekten hat. Wenn man alleine man schaut, was eine ArrayList oder ein String intern selber an Feldern hat - das wird schnell viel. Hinzu kommt Polymorphismus.

Klasse A referenziert Klasse B, aber es gibt eine Ableitung von B - Klasse C. Will man die auch durchtraversieren oder nicht?

Viel sinnvoller ist es, das sauber zu programmieren. JPA hat einen CriteriaBuilder, der für sowas gedacht ist. Spendier zu jeder Entität eine Methode, die für die Entität das User-Select Criteria bastelt. Aufwand pro Entität vermutlich < 5 Minuten. Das ist schneller umgesetzt und wartbarer, als das per Reflection zu lösen.
 

Neue Themen


Zurück
Oben