added support for spring data repositories to spring model indexing

This commit is contained in:
Martin Lippert
2023-03-31 15:15:33 +02:00
parent 123021ad03
commit e01234387a
11 changed files with 322 additions and 186 deletions

View File

@@ -30,11 +30,12 @@ public class SpringSymbolIndexerConfig {
@Bean
AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders(SymbolCache cache, SpringMetamodelIndex springIndex) {
AnnotationHierarchyAwareLookup<SymbolProvider> providers = new AnnotationHierarchyAwareLookup<>();
RequestMappingSymbolProvider requestMappingSymbolProvider = new RequestMappingSymbolProvider();
BeansSymbolProvider beansSymbolProvider = new BeansSymbolProvider(springIndex);
ComponentSymbolProvider componentSymbolProvider = new ComponentSymbolProvider(springIndex);
RestrictedDefaultSymbolProvider restrictedDefaultSymbolProvider = new RestrictedDefaultSymbolProvider();
DataRepositorySymbolProvider dataRepositorySymbolProvider = new DataRepositorySymbolProvider();
DataRepositorySymbolProvider dataRepositorySymbolProvider = new DataRepositorySymbolProvider(springIndex);
WebfluxRouterSymbolProvider webfluxRouterSymbolProvider = new WebfluxRouterSymbolProvider();
providers.put(Annotations.SPRING_REQUEST_MAPPING, requestMappingSymbolProvider);

View File

@@ -10,7 +10,6 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.beans;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
@@ -26,9 +25,7 @@ import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.SymbolKind;
import org.eclipse.lsp4j.WorkspaceSymbol;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
@@ -73,11 +70,19 @@ public class BeansSymbolProvider extends AbstractSymbolProvider {
@Override
protected void addSymbolsPass1(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
if (isMethodAbstract(node)) return;
if (node == null) return;
ASTNode parent = node.getParent();
if (parent == null || !(parent instanceof MethodDeclaration)) return;
MethodDeclaration method = (MethodDeclaration) parent;
if (isMethodAbstract(method)) return;
boolean isFunction = isFunctionBean(method);
ITypeBinding beanType = getBeanType(method);
String markerString = getAnnotations(method);
boolean isFunction = isFunctionBean(node);
ITypeBinding beanType = getBeanType(node);
String markerString = getAnnotations(node);
for (Tuple2<String, DocumentRegion> nameAndRegion : getBeanNames(node, doc)) {
try {
Location location = new Location(doc.getUri(), doc.toRange(nameAndRegion.getT2()));
@@ -92,7 +97,7 @@ public class BeansSymbolProvider extends AbstractSymbolProvider {
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), enhancedSymbol));
InjectionPoint[] injectionPoints = findInjectionPoints(node, doc);
InjectionPoint[] injectionPoints = ASTUtils.findInjectionPoints(method, doc);
Set<String> supertypes = new HashSet<>();
ASTUtils.findSupertypes(beanType, supertypes);
@@ -105,18 +110,6 @@ public class BeansSymbolProvider extends AbstractSymbolProvider {
}
}
private InjectionPoint[] findInjectionPoints(Annotation node, TextDocument doc) throws BadLocationException {
List<InjectionPoint> result = new ArrayList<>();
ASTNode parent = node.getParent();
if (parent instanceof MethodDeclaration) {
MethodDeclaration method = (MethodDeclaration) parent;
result.addAll(ASTUtils.getInjectionPointsFromMethodParams(method, doc));
}
return (InjectionPoint[]) result.toArray(new InjectionPoint[result.size()]);
}
@Override
protected void addSymbolsPass1(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
// this checks function beans that are defined as implementations of Function interfaces
@@ -187,70 +180,51 @@ public class BeansSymbolProvider extends AbstractSymbolProvider {
return literals.build();
}
protected ITypeBinding getBeanType(Annotation node) {
ASTNode parent = node.getParent();
if (parent instanceof MethodDeclaration) {
MethodDeclaration method = (MethodDeclaration) parent;
return method.getReturnType2().resolveBinding();
}
return null;
protected ITypeBinding getBeanType(MethodDeclaration method) {
return method.getReturnType2().resolveBinding();
}
private boolean isFunctionBean(Annotation node) {
ASTNode parent = node.getParent();
if (parent instanceof MethodDeclaration) {
MethodDeclaration method = (MethodDeclaration) parent;
String returnType = null;
private boolean isFunctionBean(MethodDeclaration method) {
String returnType = null;
if (method.getReturnType2().isParameterizedType()) {
ParameterizedType paramType = (ParameterizedType) method.getReturnType2();
Type type = paramType.getType();
ITypeBinding typeBinding = type.resolveBinding();
returnType = typeBinding.getBinaryName();
}
else {
returnType = method.getReturnType2().resolveBinding().getQualifiedName();
}
return FunctionUtils.FUNCTION_FUNCTION_TYPE.equals(returnType) || FunctionUtils.FUNCTION_CONSUMER_TYPE.equals(returnType)
|| FunctionUtils.FUNCTION_SUPPLIER_TYPE.equals(returnType);
if (method.getReturnType2().isParameterizedType()) {
ParameterizedType paramType = (ParameterizedType) method.getReturnType2();
Type type = paramType.getType();
ITypeBinding typeBinding = type.resolveBinding();
returnType = typeBinding.getBinaryName();
}
return false;
else {
returnType = method.getReturnType2().resolveBinding().getQualifiedName();
}
return FunctionUtils.FUNCTION_FUNCTION_TYPE.equals(returnType) || FunctionUtils.FUNCTION_CONSUMER_TYPE.equals(returnType)
|| FunctionUtils.FUNCTION_SUPPLIER_TYPE.equals(returnType);
}
private String getAnnotations(Annotation node) {
private String getAnnotations(MethodDeclaration method) {
StringBuilder result = new StringBuilder();
ASTNode parent = node.getParent();
if (parent instanceof MethodDeclaration) {
MethodDeclaration method = (MethodDeclaration) parent;
List<?> modifiers = method.modifiers();
for (Object modifier : modifiers) {
if (modifier instanceof Annotation) {
Annotation annotation = (Annotation) modifier;
IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding();
String type = annotationBinding.getAnnotationType().getBinaryName();
List<?> modifiers = method.modifiers();
for (Object modifier : modifiers) {
if (modifier instanceof Annotation) {
Annotation annotation = (Annotation) modifier;
IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding();
String type = annotationBinding.getAnnotationType().getBinaryName();
if (type != null && !Annotations.BEAN.equals(type)) {
result.append(' ');
result.append(annotation.toString());
}
if (type != null && !Annotations.BEAN.equals(type)) {
result.append(' ');
result.append(annotation.toString());
}
}
}
return result.toString();
}
private boolean isMethodAbstract(Annotation node) {
if (node != null && node.getParent() != null && node.getParent() instanceof MethodDeclaration) {
MethodDeclaration method = (MethodDeclaration) node.getParent();
List<?> modifiers = method.modifiers();
for (Object modifier : modifiers) {
if (modifier instanceof Modifier && ((Modifier) modifier).isAbstract()) {
return true;
}
private boolean isMethodAbstract(MethodDeclaration method) {
List<?> modifiers = method.modifiers();
for (Object modifier : modifiers) {
if (modifier instanceof Modifier && ((Modifier) modifier).isAbstract()) {
return true;
}
}
return false;

View File

@@ -10,23 +10,15 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.beans;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.SymbolKind;
import org.eclipse.lsp4j.WorkspaceSymbol;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
@@ -42,7 +34,6 @@ import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
/**
@@ -62,7 +53,7 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider {
@Override
protected void addSymbolsPass1(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
try {
if (!isOnAnnotationDeclaration(node)) {
if (node != null && node.getParent() != null && node.getParent() instanceof TypeDeclaration) {
EnhancedSymbolInformation enhancedSymbol = createSymbol(node, annotationType, metaAnnotations, doc);
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), enhancedSymbol));
}
@@ -77,8 +68,11 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider {
Collection<String> metaAnnotationNames = metaAnnotations.stream()
.map(ITypeBinding::getName)
.collect(Collectors.toList());
String beanName = getBeanName(node);
ITypeBinding beanType = getBeanType(node);
TypeDeclaration type = (TypeDeclaration) node.getParent();
String beanName = getBeanName(type);
ITypeBinding beanType = getBeanType(type);
Location location = new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength()));
@@ -94,7 +88,7 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider {
addon = new SymbolAddOnInformation[] {new BeansSymbolAddOnInformation(beanName, beanType.getQualifiedName())};
}
InjectionPoint[] injectionPoints = findInjectionPoints(node, doc);
InjectionPoint[] injectionPoints = ASTUtils.findInjectionPoints(type, doc);
Set<String> supertypes = new HashSet<>();
ASTUtils.findSupertypes(beanType, supertypes);
@@ -104,61 +98,6 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider {
return new EnhancedSymbolInformation(symbol, addon);
}
private InjectionPoint[] findInjectionPoints(Annotation node, TextDocument doc) throws BadLocationException {
List<InjectionPoint> result = new ArrayList<>();
ASTNode parent = node.getParent();
if (parent instanceof TypeDeclaration) {
TypeDeclaration type = (TypeDeclaration) parent;
MethodDeclaration[] methods = type.getMethods();
for (MethodDeclaration method : methods) {
if (method.isConstructor()) {
result.addAll(ASTUtils.getInjectionPointsFromMethodParams(method, doc));
}
}
FieldDeclaration[] fields = type.getFields();
for (FieldDeclaration field : fields) {
boolean autowiredField = false;
List<?> modifiers = field.modifiers();
for (Object modifier : modifiers) {
if (modifier instanceof Annotation) {
Annotation annotation = (Annotation) modifier;
String qualifiedName = annotation.resolveTypeBinding().getQualifiedName();
if (Annotations.AUTOWIRED.equals(qualifiedName)) {
autowiredField = true;
}
}
}
if (autowiredField) {
List<?> fragments = field.fragments();
for (Object fragment : fragments) {
if (fragment instanceof VariableDeclarationFragment) {
VariableDeclarationFragment varFragment = (VariableDeclarationFragment) fragment;
String fieldName = varFragment.getName().toString();
DocumentRegion region = ASTUtils.nodeRegion(doc, varFragment.getName());
Range range = doc.toRange(region);
Location fieldLocation = new Location(doc.getUri(), range);
String fieldType = field.getType().resolveBinding().getQualifiedName();
result.add(new InjectionPoint(fieldName, fieldType, fieldLocation));
}
}
}
}
}
return (InjectionPoint[]) result.toArray(new InjectionPoint[result.size()]);
}
protected String beanLabel(String searchPrefix, String annotationTypeName, Collection<String> metaAnnotationNames, String beanName, String beanType) {
StringBuilder symbolLabel = new StringBuilder();
symbolLabel.append("@");
@@ -186,34 +125,13 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider {
return symbolLabel.toString();
}
private String getBeanName(Annotation node) {
ASTNode parent = node.getParent();
if (parent instanceof TypeDeclaration) {
TypeDeclaration type = (TypeDeclaration) parent;
String beanName = type.getName().toString();
return BeanUtils.getBeanNameFromType(beanName);
}
return null;
private String getBeanName(TypeDeclaration type) {
String beanName = type.getName().toString();
return BeanUtils.getBeanNameFromType(beanName);
}
private ITypeBinding getBeanType(Annotation node) {
ASTNode parent = node.getParent();
if (parent instanceof TypeDeclaration) {
TypeDeclaration type = (TypeDeclaration) parent;
return type.resolveBinding();
}
return null;
private ITypeBinding getBeanType(TypeDeclaration type) {
return type.resolveBinding();
}
private boolean isOnAnnotationDeclaration(Annotation node) {
ASTNode parent = node.getParent();
if (parent != null && parent instanceof AnnotationTypeDeclaration) {
return true;
}
return false;
}
}

View File

@@ -10,6 +10,9 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.data;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.Location;
@@ -18,6 +21,8 @@ import org.eclipse.lsp4j.WorkspaceSymbol;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.index.InjectionPoint;
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
import org.springframework.ide.vscode.boot.java.beans.BeanUtils;
import org.springframework.ide.vscode.boot.java.beans.BeansSymbolAddOnInformation;
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
@@ -39,22 +44,41 @@ import reactor.util.function.Tuples;
public class DataRepositorySymbolProvider extends AbstractSymbolProvider {
private static final Logger log = LoggerFactory.getLogger(DataRepositorySymbolProvider.class);
private final SpringMetamodelIndex springIndex;
public DataRepositorySymbolProvider(SpringMetamodelIndex springIndex) {
this.springIndex = springIndex;
}
@Override
protected void addSymbolsPass1(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
// this checks spring data repository beans that are defined as extensions of the repository interface
Tuple4<String, ITypeBinding, String, DocumentRegion> repositoryBean = getRepositoryBean(typeDeclaration, doc);
if (repositoryBean != null) {
try {
String beanName = repositoryBean.getT1();
ITypeBinding beanType = repositoryBean.getT2();
Location location = new Location(doc.getUri(), doc.toRange(repositoryBean.getT4()));
WorkspaceSymbol symbol = new WorkspaceSymbol(
beanLabel(true, repositoryBean.getT1(), repositoryBean.getT2().getName(), repositoryBean.getT3()),
beanLabel(true, beanName, beanType.getName(), repositoryBean.getT3()),
SymbolKind.Interface,
Either.forLeft(new Location(doc.getUri(), doc.toRange(repositoryBean.getT4()))));
Either.forLeft(location));
SymbolAddOnInformation[] addon = new SymbolAddOnInformation[] {new BeansSymbolAddOnInformation(repositoryBean.getT1(), repositoryBean.getT2().getQualifiedName())};
EnhancedSymbolInformation enhancedSymbol = new EnhancedSymbolInformation(symbol, addon);
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), enhancedSymbol));
InjectionPoint[] injectionPoints = ASTUtils.findInjectionPoints(typeDeclaration, doc);
Set<String> supertypes = new HashSet<>();
ASTUtils.findSupertypes(beanType, supertypes);
String concreteRepoType = typeDeclaration.resolveBinding().getQualifiedName();
springIndex.registerBean(beanName, concreteRepoType, location, injectionPoints, (String[]) supertypes.toArray(new String[supertypes.size()]));
} catch (BadLocationException e) {
log.error("error creating data repository symbol for a specific range", e);
}

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.boot.java.utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
@@ -24,6 +25,7 @@ import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
@@ -37,6 +39,7 @@ import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Range;
import org.slf4j.Logger;
@@ -381,5 +384,77 @@ public class ASTUtils {
}
}
}
public static InjectionPoint[] findInjectionPoints(MethodDeclaration method, TextDocument doc) throws BadLocationException {
List<InjectionPoint> result = new ArrayList<>();
List<?> parameters = method.parameters();
for (Object object : parameters) {
if (object instanceof VariableDeclaration) {
VariableDeclaration variable = (VariableDeclaration) object;
String name = variable.getName().toString();
String type = variable.resolveBinding().getType().getQualifiedName();
DocumentRegion region = ASTUtils.nodeRegion(doc, variable.getName());
Range range = doc.toRange(region);
Location location = new Location(doc.getUri(), range);
result.add(new InjectionPoint(name, type, location));
}
}
return (InjectionPoint[]) result.toArray(new InjectionPoint[result.size()]);
}
public static InjectionPoint[] findInjectionPoints(TypeDeclaration type, TextDocument doc) throws BadLocationException {
List<InjectionPoint> result = new ArrayList<>();
MethodDeclaration[] methods = type.getMethods();
for (MethodDeclaration method : methods) {
if (method.isConstructor()) {
result.addAll(Arrays.asList(ASTUtils.findInjectionPoints(method, doc)));
}
}
FieldDeclaration[] fields = type.getFields();
for (FieldDeclaration field : fields) {
boolean autowiredField = false;
List<?> modifiers = field.modifiers();
for (Object modifier : modifiers) {
if (modifier instanceof Annotation) {
Annotation annotation = (Annotation) modifier;
String qualifiedName = annotation.resolveTypeBinding().getQualifiedName();
if (Annotations.AUTOWIRED.equals(qualifiedName)) {
autowiredField = true;
}
}
}
if (autowiredField) {
List<?> fragments = field.fragments();
for (Object fragment : fragments) {
if (fragment instanceof VariableDeclarationFragment) {
VariableDeclarationFragment varFragment = (VariableDeclarationFragment) fragment;
String fieldName = varFragment.getName().toString();
DocumentRegion region = ASTUtils.nodeRegion(doc, varFragment.getName());
Range range = doc.toRange(region);
Location fieldLocation = new Location(doc.getUri(), range);
String fieldType = field.getType().resolveBinding().getQualifiedName();
result.add(new InjectionPoint(fieldName, fieldType, fieldLocation));
}
}
}
}
return (InjectionPoint[]) result.toArray(new InjectionPoint[result.size()]);
}
}

View File

@@ -87,24 +87,6 @@ public class SpringMetamodelIndexerBeansTest {
assertEquals("org.test.BeanClass1", beans[0].getType());
}
@Test
void testBeansNameAndTypeFromComponentAnnotatedClassExists() {
Bean[] beans = springIndex.getBeans("constructorInjectionService");
assertEquals(1, beans.length);
assertEquals("constructorInjectionService", beans[0].getName());
assertEquals("org.test.injections.ConstructorInjectionService", beans[0].getType());
}
@Test
void testBeansNameAndTypeFromConfigurationAnnotatedClassExists() {
Bean[] beans = springIndex.getBeans("configurationWithoutInjection");
assertEquals(1, beans.length);
assertEquals("configurationWithoutInjection", beans[0].getName());
assertEquals("org.test.injections.ConfigurationWithoutInjection", beans[0].getType());
}
@Test
void testBeansDefintionLocationFromBeanAnnotatedMethod() {
Bean[] beans = springIndex.getBeans("bean1");
@@ -114,6 +96,15 @@ public class SpringMetamodelIndexerBeansTest {
assertEquals(location, beans[0].getLocation());
}
@Test
void testBeansNameAndTypeFromComponentAnnotatedClassExists() {
Bean[] beans = springIndex.getBeans("constructorInjectionService");
assertEquals(1, beans.length);
assertEquals("constructorInjectionService", beans[0].getName());
assertEquals("org.test.injections.ConstructorInjectionService", beans[0].getType());
}
@Test
void testBeansDefintionLocationFromComponentAnnotatedClass() {
Bean[] beans = springIndex.getBeans("constructorInjectionService");
@@ -123,6 +114,15 @@ public class SpringMetamodelIndexerBeansTest {
assertEquals(location, beans[0].getLocation());
}
@Test
void testBeansNameAndTypeFromConfigurationAnnotatedClassExists() {
Bean[] beans = springIndex.getBeans("configurationWithoutInjection");
assertEquals(1, beans.length);
assertEquals("configurationWithoutInjection", beans[0].getName());
assertEquals("org.test.injections.ConfigurationWithoutInjection", beans[0].getType());
}
@Test
void testBeansDefinitionLocationFromConfigurationAnnotatedClass() {
Bean[] beans = springIndex.getBeans("configurationWithoutInjection");
@@ -203,6 +203,18 @@ public class SpringMetamodelIndexerBeansTest {
Location ip2Location = new Location(docUri, new Range(new Position(11, 31), new Position(11, 36)));
assertEquals(ip2Location, injectionPoints[1].getLocation());
}
@Test
void testBeanFromSpringDataRepository() {
Bean[] beans = springIndex.getBeans("customerRepository");
assertEquals(1, beans.length);
assertEquals("customerRepository", beans[0].getName());
assertEquals("org.test.springdata.CustomerRepository", beans[0].getType());
InjectionPoint[] injectionPoints = beans[0].getInjectionPoints();
assertEquals(0, injectionPoints.length);
}
@Test
void testBeansWithSupertypes() {
@@ -218,7 +230,8 @@ public class SpringMetamodelIndexerBeansTest {
assertFalse(beans[0].isTypeCompatibleWith("java.lang.String"));
assertFalse(beans[0].isTypeCompatibleWith("java.util.Comparator"));
}
}

View File

@@ -11,17 +11,22 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.8</version>
<version>3.0.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<jakarta-servlet.version>5.0.0</jakarta-servlet.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
@@ -34,6 +39,15 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<!--
<build>

View File

@@ -0,0 +1,49 @@
// tag::sample[]
package org.test.springdata;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
@Entity
public class Customer extends Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private boolean thisCustomerIsSpecial;//contains keyword in name
@ManyToOne
private Employee responsibleEmployee;
protected Customer() {
super();
}
public Customer(String firstName, String lastName, Employee responsibleEmployee) {
super(firstName, lastName);
this.responsibleEmployee = responsibleEmployee;
}
@Override
public String toString() {
return "Customer [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", responsibleEmployee="
+ responsibleEmployee + "]";
}
// end::sample[]
public Long getId() {
return id;
}
public boolean isThisCustomerIsSpecial() {
return thisCustomerIsSpecial;
}
public Employee getResponsibleEmployee() {
return responsibleEmployee;
}
}

View File

@@ -0,0 +1,10 @@
package org.test.springdata;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}

View File

@@ -0,0 +1,33 @@
// tag::sample[]
package org.test.springdata;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
public class Employee extends Person {
@Id
private Long socialSecurityNumber;
protected Employee() {
}
public Employee(long socialSecurityNumber, String firstName, String lastName) {
super(firstName, lastName);
this.socialSecurityNumber = socialSecurityNumber;
}
@Override
public String toString() {
return "Employee[socialSecurityNumber=%d, firstName='%s', lastName='%s']".formatted(
socialSecurityNumber, firstName, lastName);
}
// end::sample[]
public Long getSocialSecurityNumber() {
return socialSecurityNumber;
}
}

View File

@@ -0,0 +1,25 @@
package org.test.springdata;
public class Person {
protected String firstName;
protected String lastName;
protected Person() {
}
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}