I have designed my database to use the inheritance strategy of Table Per Class where the common fields exists in the derived tables as well as in the parent table. Some simple model would look like this.
Vehicle.java
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Vehicle {
#Id #GeneratedValue
private int id;
private String name;
private String brand;
}
Car.java
#Entity
public class Car extends Vehicle {
private String oil;
}
Bike.java
#Entity
public class Bike extends Vehicle {
private String frame;
}
Now, I would like to have a unified VehicleRepository for all kinds of Vehicle objects, including Car and Bike.
The repository could look something like this.
VehicleRepository.java
#Repository
public interface VehicleRepository extends CrudRepository<Vehicle, Integer> {
#Query("SELECT v FROM Vehicle e WHERE v.vehicleId = ?1")
public Vehicle findById(Integer vehicleId);
}
So I'd like to be able to do something like following.
int carId = 3; // I know that in the DB there's a car with that ID
int bikeId = 7; // I know that in the DB there's a bike with that ID
// the important part
Car car = (Car) repo.findById(carId);
Bike bike = (Bike) repo.findById(bikeId);
// now I want to be able to call:
car.getOil();
bike.getFrame();
The question is: would this work and is this a good pattern to do it?
You can include all the fields in the Vehicle and try to use projections for Car and Bike.
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Vehicle {
#Id #GeneratedValue
private int id;
private String name;
private String brand;
private String oil;
private String frame;
}
For Car and Bike you can use projections:
public interface VehicleProjection {
public int getId();
public String getBrand();
public String getName();
}
public interface Car extends VehicleProjection {
public String getOil();
}
public interface Bike extends VehicleProjection {
public String getFrame();
}
And your repository should look like this:
public interface CarRepository extends Repository<Vehicle, Integer> {
public Car findById(Integer carId);
}
Same for Bike repository
Related
I have these classes:
enum Brand {
FORD, FERRARI, TESLA, RENAULT;
}
public class Car {
Brand brand;
String plate;
...
}
//getters and setters
Imagine that for some reason, I need to make Car a superclass for two new classes: CombustionCar and ElectricCar. One of the new requierements is that ElectricCar's brand attribute must be always TESLA value and not any of the other ones values.
I've thougth some solutions:
I could keep Brand attr on superclass Car, and make ElectricCar constructor to set TESLA brand. But this way could allow me to set a new Brand after creating the object
public class ElectricCar extends Car {
public ElectricCar(...){
super(Brand.TESLA, ...);
}
ElectricCar ec = new ElectricCar(...);
ec.setBrand(Brand.FORD);
I can take Brand attr out from superclass and set it on both subclasses, but setting it in ElectricCar as a class attribute with a final so anyone would be able to set a new value
public class ElectricCar extends Car {
public static final Brand brand = Brand.TESLA;
...
}
public class CombustionCar extends Car {
private Brand brand;
...
}
Avoid inheritance and use composition, but with this I wont be able to use, for example, a List which contain both:
public class ElectricCar {
private Car car;
private Brand brand = Brand.TESLA;//with no setter
...
}
public class CombustionCar {
private Car car;
private Brand brand;
...
}
I'm asking for the most elegant and manteinable solution, I think any of them would be nice to resolve my problem.
Your first solution is incorrect given that you required a non editable BRAND for an electric car.
Your second solution just doesn't work at all excepted if you override both getter and setter of brand field to use your static field, which is not "elegant and mantainable"
Your third solution doesn't make use of object oriented concept.
A simple solution I would use is to let the field brand and its getter in Car superclass, but I'd only define the setter in the CombustionCar class.
Alternatively, if you extend your model, you could create an intermediate abstract superclass "FreeBrandCar" which implements the setter.
Solution with the setter in CombustionCar
abstract public class Car {
protected String brand;
protected Car(final String b) {
this.brand = b;
}
public String getBrand() {
return this.brand;
}
}
public class ElectricCar extends Car {
public ElectricCar() {
super("Tesla");
}
}
public class CombustionCar extends Car {
public CombustionCar(final String b) {
super(b);
}
public void setBrand(final String b) {
this.brand = b;
}
}
Solution with an intermediate class
abstract public class Car {
protected String brand;
protected Car(final String b) {
this.brand = b;
}
public String getBrand() {
return this.brand;
}
}
abstract public class FreeBrandCar extends Car {
public FreeBrandCar (final String b) {
super(b);
}
public void setBrand(final String b) {
this.brand = b;
}
}
public class ElectricCar extends Car {
public ElectricCar() {
super("Tesla");
}
}
public class CombustionCar extends FreeBrandCar {
public CombustionCar(final String b) {
super(b);
}
}
It respects your requirements :
public void test() {
ElectricCar ec = new ElectricCar();
ec.setBrand("..."): // Doesn't compile
CombustionCar cc = new CombustionCar("Ford"); // OK
cc.setBrand("Fiat"); // OK
Arrays.asList(ec, cc)
.stream()
.forEach(car -> System.out.println(car.getBrand())); // prints Tesla and Fiat
}
Based on my research we have two way for getting related data from two or more tables.
For example if we have 2 tables like below:
#Entity
public class User {
#PrimaryKey public long userId;
public String name;
public int age;
}
#Entity
public class Library {
#PrimaryKey public long libraryId;
public long userOwnerId;
}
If we want to load all data we have two options:
1. #Embedded and #Relation
By adding this class:
public class UserAndLibrary {
#Embedded public User user;
#Relation(
parentColumn = "userId",
entityColumn = "userOwnerId"
)
public Library library;
}
And add DAO method:
#Transaction
#Query("SELECT * FROM User")
public List<UserAndLibrary> getUsersAndLibraries();
More information in Android Documentation
2. #DatabaseView
#DatabaseView("SELECT user.id, user.name, user.age, " +
"library.libraryId FROM user " +
"INNER JOIN library ON user.userId = library.libraryId)
public class UserAndLibrary {
public long userId;
public String name;
public int age;
public long libraryId;
}
and a associating
#Database(entities = {User.class, Library.class},
views = {UserAndLibrary.class},
version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
What is the difference between two options?
Let's say I want to mock a class with the following structure, for testing purposes:
#Data
public class Street() {
private House house;
#Data
static class House {
private List<Room> rooms;
}
#Data
static class Room {
private Door door;
}
#Data
static class Door {
private String material;
}
}
What would be the best way to concisely create a Street object, containing a House that has a Room with a wooden door?
I was thinking of adding a Lombok #Builder annotation to the Street class, but discovered I would also need builders for each of the nested classes, and was wondering if there would be a cleaner way to achieve this.
Use #SuperBuilder from lombok project
Assume you have :
#Getter
#SuperBuilder
public class Parent {
private final String parentName;
private final int parentAge;
}
#Getter
#SuperBuilder
public class Child extends Parent {
private final String childName;
private final int childAge;
}
#Getter
#SuperBuilder
public class Student extends Child {
private final String schoolName;
}
Then you can create a new Student like this :
Student student = Student.builder()
.parentName("Andrea")
.parentAge(38)
.childName("Emma")
.childAge(6)
.schoolName("Baeldung High School")
.build();
Reference : https://www.baeldung.com/lombok-builder-inheritance#lombok-builder-and-inheritance-1
My only suggestion is this:
public class Street {
private House house;
private Room room;
private Door door;
public class House{
//build class House
}
public class Room{
//build class Room
}
public class Door{
//build class Door
}
public Street(House house, Room room, Door door) {
this.house = new House();
this.room = new Room();
this.door = new Door();
}
}
and then you can work inside the Street class with the objects you have to access methods of the other little classed nested into Street
I have the following class structure:
public abstract class Creature{
private String name;
//strategy pattern composition
private SkillInterface skill;
}
public interface SkillInterface {
void attack();
}
public class NoSkill implements SkillInterface {
#Override
public void attack() {
//statements
}
}
My goal is to persist Creature objects at one table in database. Subclasses of SkillInterface are without any fields. As they determine the behaviour, I want to convert selected SkillInterface class name to a String, as I only need to persist the classname of the current skill strategy of creature, with a String like skill.getClass().getSimpleName(). I tried to implement it with #Converter annotation, using AttributeConverter class to convert SkillInterface to String and save, but always had mapping exceptions. I want to be able to save it as String and retrieve as SkillInterface object.
But how can I implement it with Hibernate? Or do I have a design mistake?
Ok looks like I have found a basic solution that can be used to persist Strategy Pattern interfaces implementations. I used a #Converter annotation and a AttributeConverter class to convert strategy class names to column while saving to database and cast the retrieved String back to strategy class as following:
#Entity
public class Creature {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Convert(converter = SkillConverter.class)
private SkillInterface skill;
}
public class SkillConverter implements AttributeConverter<SkillInterface,String> {
#Override
public String convertToDatabaseColumn(SkillInterface skill) {
return skill.getClass().getSimpleName().toLowerCase();
}
#Override
public SkillInterface convertToEntityAttribute(String dbData) {
//works as a factory
if (dbData.equals("noskill")) {
return new NoSkill();
} else if (dbData.equals("axe")) {
return new Axe();
}
return null;
}
}
public interface SkillInterface {
public String getSkill();
void attack();
}
public class NoSkill implements SkillInterface{
public String getSkill() {
return getClass().getSimpleName();
}
#Override
public void attack() {
//strategy statements
}
}
You can use a proxy field to this for you like below:
abstract class Creature {
#Column
private String name;
// strategy pattern composition
private SkillInterface skill;
#Column
private String skillName;
public String getSkillName() {
return skill.getClass().getSimpleName();
}
public void setSkillName(String skillName) {
//ignore
}
}
Here is a simplified working code. There are a mapped superclass and two its subclasses (in real life superclass of course contains more fields)
Animal.java
#MappedSuperclass
#lombok.NoArgsConstructor
#lombok.RequiredArgsConstructor
public abstract class Animal {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#lombok.Getter
private Long id;
#lombok.Getter
#lombok.NonNull
private String name;
}
Cat.java
#Entity
#Table
#lombok.NoArgsConstructor
public class Cat extends Animal {
public Cat(Integer weight, String name) {
super(name);
this.weight = weight;
}
#lombok.Getter
private Integer weight;
}
Dog.java
#Entity
#Table
#lombok.NoArgsConstructor
public class Dog extends Animal {
public Dog(Integer age, String name) {
super(name);
this.age = age;
}
#lombok.Getter
private Integer age;
}
AnimalRepositoryImpl and AnimalRepository contain some shared code for Cat and Dog repositories.
AnimalRepository.java
#NoRepositoryBean
public interface AnimalRepository<T extends Animal> extends JpaRepository<T, Long> {
List<T> findAllByName(String name);
}
AnimalRepositoryImpl.java
public class AnimalRepositoryImpl<T extends Animal> {
#Autowired
AnimalRepository<T> animalRepository;
public List<T> findAllBySomeLogic() {
return animalRepository.findAll().stream().filter(animal -> !animal.getName().startsWith("Z")).collect(Collectors.toList());
}
}
Now I can add all CatRepositories and it still works (and works correctly).
CatRepository.java
#Transactional
public interface CatRepository extends AnimalRepository<Cat>, CatRepositoryCustom {
}
CatRepositoryCustom.java
public interface CatRepositoryCustom {
public List<Cat> findAllBySomeLogic();
}
CatRepositoryImpl.java
public class CatRepositoryImpl extends AnimalRepositoryImpl implements CatRepositoryCustom {
}
Here is a test class which still uses only cat repository.
AnimalRepositoryTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestConfiguration.class)
#ActiveProfiles(profiles = "test")
public class AnimalRepositoryTest {
#After
public void tearDown() {
catRepository.deleteAll();
}
#Autowired
private CatRepository catRepository;
#Test
public void shouldFindAllBySomeLogic() {
// given
catRepository.save(Lists.newArrayList(new Cat(2000, "Luna"), new Cat(2500, "Zoe"), new Cat(1800, "Toby")));
// when
List<Cat> cats = catRepository.findAllBySomeLogic();
// then
assertThat(cats.stream().map(c -> c.getName()).collect(Collectors.toList()), containsInAnyOrder("Luna", "Toby"));
}
#Test
public void shouldFindAllByName() {
// given
catRepository.save(Lists.newArrayList(new Cat(2000, "Luna"), new Cat(2500, "Zoe"), new Cat(1800, "Toby")));
// when
List<Cat> cats = catRepository.findAllByName("Luna");
// then
assertThat(cats.stream().map(c -> c.getName()).collect(Collectors.toList()), containsInAnyOrder("Luna"));
}
}
The way I've coded it was inspired mostly by this question (but my case is more complicated).
So... the main question. - How to add repositories for Dog (almost identical to Cat ones) and not to get something like NoUniqueBeanDefinitionException: No qualifying bean of type...? I've tried some variations with #Qualifier but seems it doesn't work in this case. Or maybe I'm doing it completely wrong.
I see at least one failure related to the generic definition of your classes. The class CatRepositoryImpl extends the classe AnimalRepositoryImpl without any generic Types. (See the following two code snippets of your post)
public class CatRepositoryImpl extends AnimalRepositoryImpl implements CatRepositoryCustom {
}
public class AnimalRepositoryImpl<T extends Animal> {
}
In my opinion it should look like.
public class CatRepositoryImpl extends AnimalRepositoryImpl<Cat> implements CatRepositoryCustom {
}
Beside that, I would avoid doing logic related things in a Repository class and move it to a Service level.