Welcome To Fusebes - Dev & Programming Blog

Kotlin Infix Notation - Make function calls more intuitive
07
Mar
2021

Kotlin Infix Notation – Make function calls more intuitive

Kotlin supports method calls of a special kind, called infix calls.

You can mark any member function or extension function with the infix modifier to allow it to be called using infix notation. The only requirement is that the function should have only one required parameter.

Infix notations are used extensively in Kotlin. If you’ve been programming in Kotlin, chances are that you’ve already used infix notations.

Following are few common examples of infix notations in Kotlin –

1. Infix Notation Example – Creating a Map

val map = mapOf(1 to "one", 2 to "two", 3 to "three")

In the above example, the expressions 1 to "one"2 to "two" etc, are infix notations of the function calls 1.to("one") and 2.to("two") etc.

to() is an infix function that creates a Pair<A, B> from two values.

2. Infix Notation Example – Range Operators (until, downTo, step)

Kotlin provides various range operators that are usually called using infix notation –

for(i in 1 until 10) {	// Same as - for(i in 1.until(10))
    print("$i ")
}
for(i in 10 downTo 1) {	 // Same as - for(i in 10.downTo(1))
    print("$i ")
}
for(i in 1 until 10 step 2) { // Same as - for(i in 1.until(10).step(2))
    print("$i ")
}

3. Infix Notation Example – String.matches()

The String.matches() function in Kotlin which matches a String with a Regex is an infix function –

val regex = Regex("[tT]rue|[yY]es")
val str = "yes"

str.matches(regex)

// Infix notation of the above function call -
str matches regex

Creating an Infix Function

You can make a single argument member function or extension function, an infix function by marking it with the infix keyword.

Check out the following example where I have created an infix member function called add() for adding two Complex numbers –

data class ComplexNumber(val realPart: Double, val imaginaryPart: Double) {
	// Infix function for adding two complex numbers
    infix fun add(c: ComplexNumber): ComplexNumber {
        return ComplexNumber(realPart + c.realPart, imaginaryPart + c.imaginaryPart)
    }
}

You can now call the add() method using infix notation –

val c1 = ComplexNumber(3.0, 5.0)
val c2 = ComplexNumber(4.0, 7.0)

// Usual call
c1.add(c2) // produces - ComplexNumber(realPart=7.0, imaginaryPart=12.0)

// Infix call
c1 add c2  // produces - ComplexNumber(realPart=7.0, imaginaryPart=12.0)

Conclusion

That’s all folks. In this article, You learned what infix notation is and how it works. You saw several examples of Infix notations in Kotlin and also learned how to create an infix function.

Thanks for reading. Please ask any doubts in the comment section below.

Introduction to Data Classes in Kotlin
07
Mar
2021

Introduction to Data Classes in Kotlin

While building any application, we often need to create classes whose primary purpose is to hold data/state. These classes generally contain the same old boilerplate code in the form of getterssettersequals()hashcode() and toString() methods.

Motivation

Consider the following example of a Customer class in Java that just holds data about a Customer and doesn’t have any functionality whatsoever –

public class Customer {
    private String id;
    private String name;

    public Customer(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Customer customer = (Customer) o;

        if (id != null ? !id.equals(customer.id) : customer.id != null) return false;
        return name != null ? name.equals(customer.name) : customer.name == null;
    }

    @Override
    public int hashCode() {
        int result = id != null ? id.hashCode() : 0;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }
}

You see, for creating a Simple class with only two member fields, we had to write almost 50 lines of code.

Yes, I know that you don’t need to write that code yourself and any good IDE can generate all that boilerplate code for you.

But that code will still be there in your source file and clutter it. Moreover, whenever you add a new member field to the Class, you’ll need to regenerate/modify the constructors, getters/setters and equals()/hashcode() methods.

You can also use a third party library like Project Lombok to generate getters/settersequals()/hashCode()toString() methods and more. But there is no out of the box solution without any library that can help us avoid these boilerplate codes in our application.

Kotlin Data Classes

Kotlin has a better solution for classes that are used to hold data/state. It’s called a Data Class. A Data Class is like a regular class but with some additional functionalities.

With Kotlin’s data classes, you don’t need to write/generate all the lengthy boilerplate code yourself. The compiler automatically generates a default getter and setter for all the mutable properties, and a getter (only) for all the read-only properties of the data class. Moreover, It also derives the implementation of standard methods like equals()hashCode() and toString() from the properties declared in the data class’s primary constructor.

For example, The Customer class that we wrote in the previous section in Java can be written in Kotlin in just one line –

data class Customer(val id: Long, val name: String)

Accessing the properties of the data class

The following example shows how you can access the properties of the data class –

val customer = Customer(1, "Sachin")

// Getting a property
val name = customer.name

Since all the properties of the Customer class are immutable, there is no default setter generated by the compiler. Therefore, If you try to set a property, the compiler will give an error –

// Setting a Property

// You cannot set read-only properties
customer.id = 2	// Error: Val cannot be assigned

Let’s now see how we can use the equals()hashCode(), and toString() methods of the data class-

1. Data class’s equals() method

val customer1 = Customer(1, "John")
val customer2 = Customer(1, "John")

println(customer1.equals(customer2))  // Prints true

You can also use Kotlin’s Structural equality operator == to check for equality. The == operator internally calls the equals() method –

println(customer1 == customer2)  // Prints true

2. Data class’s toString() method

The toString() method converts the object to a String in the form of "ClassName(field1=value1, field2=value)" –

val customer = Customer(2, "Robert")
println("Customer Details : $customer")
# Output
Customer Details : Customer(id=2, name=Robert)

3. Data class’s hashCode() method

val customer = Customer(2, "Robert")
println("Customer HashCode : ${customer.hashCode()}") // Prints -1841845792

Apart from the standard methods like equals()hashCode() and toString(), Kotlin also generates a copy() function and componentN() functions for all the data classes. Let’s understand what these functions do and how to use them –

Data Classes and Immutability: The copy() function

Although the properties of a data class can be mutable (declared using var), It’s strongly recommended to use immutable properties (declared using val) so as to keep the instances of the data class immutable.

Immutable objects are easier to work with and reason about while working with multi-threaded applications. Since they can not be modified after creation, you don’t need to worry about concurrency issues that arise when multiple threads try to modify an object at the same time.

Kotlin makes working with immutable data objects easier by automatically generating a copy() function for all the data classes. You can use the copy() function to copy an existing object into a new object and modify some of the properties while keeping the existing object unchanged.

The following example shows how copy() function can be used –

val customer = Customer(3, "James")

/* 
   Copies the customer object into a separate Object and updates the name. 
   The existing customer object remains unchanged.
*/
val updatedCustomer = customer.copy(name = "James Altucher")
println("Customer : $customer")
println("Updated Customer : $updatedCustomer")
# Output
Customer : Customer(id=3, name=James)
Updated Customer : Customer(id=3, name=James Altucher)

Data Classes and Destructuring Declarations: The componentN() functions

Kotlin also generates componentN() functions corresponding to all the properties declared in the primary constructor of the data class.

For the Customer data class that we defined in the previous section, Kotlin generates two componentN() functions – component1() and component2() corresponding to the id and name properties –

val customer = Customer(4, "Joseph")

println(customer.component1()) // Prints 4
println(customer.component2()) // Prints "Joseph"

The component functions enable us to use the so-called Destructuring Declaration in Kotlin. The Destructuring declaration syntax helps you destructure an object into a number of variables like this –

val customer = Customer(4, "Joseph")

// Destructuring Declaration
val (id, name) = customer
println("id = $id, name = $name") // Prints "id = 4, name = Joseph"

Requirements for Data Classes

Every Data Class in Kotlin needs to fulfill the following requirements –

  • The primary constructor must have at least one parameter
  • All the parameters declared in the primary constructor need to be marked as val or var.
  • Data classes cannot be abstract, open, sealed or inner.

Conclusion

Data classes help us avoid a lot of common boilerplate code and make the classes clean and concise. In this article, you learned how data classes work and how to use them. I hope you understood the all the concepts presented in this article.

Three Number Sum Solution
06
Feb
2021

Three Number Sum Solution

Three Number Sum Problem Statement

Given an array of integers, find all triplets in the array that sum up to a given target value.

In other words, given an array arr and a target value target, return all triplets a, b, c such that a + b + c = target.

Example:

Input array: [7, 12, 3, 1, 2, -6, 5, -8, 6]
Target sum: 0

Output: [[2, -8, 6], [3, 5, -8], [1, -6, 5]]

Three Number Sum Problem solution in Java

METHOD 1. Naive approach: Use three for loops

The naive approach is to just use three nested for loops and check if the sum of any three elements in the array is equal to the given target.

Time complexity: O(n^3)

import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;

class ThreeSum {

  // Time complexity: O(n^3)
  private static List<Integer[]> findThreeSum_BruteForce(int[] nums, int target) {
    List<Integer[]> result = new ArrayList<>();
    for (int i = 0; i < nums.length; i++) {
      for (int j = i + 1; j < nums.length; j++) {
        for (int k = j + 1; k < nums.length; k++) {
          if (nums[i] + nums[j] + nums[k] == target) {
            result.add(new Integer[] { nums[i], nums[j], nums[k] });
          }
        }
      }
    }
    return result;
  }

  public static void main(String[] args) {
    Scanner keyboard = new Scanner(System.in);

    int n = keyboard.nextInt();
    int[] nums = new int[n];

    for (int i = 0; i < n; i++) {
      nums[i] = keyboard.nextInt();
    }
    int target = keyboard.nextInt();

    keyboard.close();

    List<Integer[]> result = findThreeSum_Sorting(nums, target);

    for(Integer[] triplets: result) {
      for(int num: triplets) {
        System.out.print(num + " ");
      }
      System.out.println();
    }
  }
}

METHOD 2. Use Sorting along with the two-pointer sliding window approach

Another approach is to first sort the array, then –

  • Iterate through each element of the array and for every iteration,
    • Fix the first element (nums[i])
    • Try to find the other two elements whose sum along with nums[i] gives target. This boils down to the two sum problem.

Time complexity: O(n^2)

import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;

class ThreeSum {

  // Time complexity: O(n^2)
  private static List<Integer[]> findThreeSum_Sorting(int[] nums, int target) {
    List<Integer[]> result = new ArrayList<>();
    Arrays.sort(nums);
    for (int i = 0; i < nums.length; i++) {
      int left = i + 1;
      int right = nums.length - 1;
      while (left < right) {
        if (nums[i] + nums[left] + nums[right] == target) {
          result.add(new Integer[] { nums[i], nums[left], nums[right] });
          left++;
          right--;
        } else if (nums[i] + nums[left] + nums[right] < target) {
          left++;
        } else {
          right--;
        }
      }
    }
    return result;
  }
}

METHOD 3. Use a Map/Set

Finally, you can also solve the problem using a Map/Set. You just need to iterate through the array, fix the first element, and then try to find the other two elements using the approach similar to the two sum problem.

I’m using a Set in the following solution instead of a Map as used in the two-sum problem because in the two-sum problem, we had to keep track of the index of the elements as well. But In this problem, we just care about the element and not its index.

Time complexity: O(n^2)

import java.util.Set;
import java.util.Scanner;
import java.util.HashSet;

class ThreeSum {

  // Time complexity: O(n^2)
  private static List<Integer[]> findThreeSum(int[] nums, int target) {
    List<Integer[]> result = new ArrayList<>();
    for (int i = 0; i < nums.length; i++) {
      int currentTarget = target - nums[i];
      Set<Integer> existingNums = new HashSet<>();
      for (int j = i + 1; j < nums.length; j++) {
        if (existingNums.contains(currentTarget - nums[j])) {
          result.add(new Integer[] { nums[i], nums[j], currentTarget - nums[j] });
        } else {
          existingNums.add(nums[j]);
        }
      }
    }
    return result;
  }
}

Liked the Article? Share it on Social media!

Kotlin Type Checks and Smart Casts
07
Mar
2021

Kotlin Type Checks and Smart Casts

When working with mixed types, We often need to know the type of an object at runtime so that we can safely cast the object to our desired type and call methods or access properties on it.

Type Checks

In Kotlin, You can check whether an object is of a certain type at runtime by using the is operator.

Following is an example that demonstrates the usage of is operator.

fun main(args: Array<String>) {
    val mixedTypeList: List<Any> = listOf("I", "am", 5, "feet", 9.5, "inches", "tall")

    for(value in mixedTypeList) {
        if (value is String) {
            println("String: '$value' of length ${value.length} ")
        } else if (value is Int) {
            println("Integer: '$value'")
        } else if (value is Double) {
            println("Double: '$value' with Ceil value ${Math.ceil(value)}")
        } else {
            println("Unknown Type")
        }
    }
}

In the example above, We have a list containing mixed types. We iterate through the list, check the type of each value in the list using the is operator and print the details about the value.

Following is the output of the above program –

# Output
String: 'I' of length 1 
String: 'am' of length 2 
Integer: 5
String: 'feet' of length 4 
Double: 9.5 with Ceil value 10.0
String: 'inches' of length 6 
String: 'tall' of length 4 

Note that you can simplify the above program further by replacing the if-else block with a when expression like this –

for(value in mixedTypeList) {
    when(value) {
        is String -> println("String: '$value' of length ${value.length} ")
        is Int -> println("Integer: $value")
        is Double -> println("Double: $value with Ceil value ${Math.ceil(value)}")
        else -> println("Unknown Type")
    }
}

The is operator also has a negated form !is. Here is an example of !is operator –

if(value !is String) {
    println("Not a String")
}

Smart Casts

The examples described in the previous section uses a feature of Kotlin called Smart Cast. To understand how Smart Cast work in Kotlin, Let’s compare how we do class casting in Java vs Kotlin.

In Java, We first check the type of the variable using the instanceof operator and then cast it to the target type like this –

Object obj = "The quick brown fox jumped over a lazy dog";
if(obj instanceof String) {
    // Explicit Casting to `String`
    String str = (String) obj;
    System.out.println("Found a String of length " + str.length());
}

But In Kotlin, When you perform an is or !is check on a variable, the compiler tracks this information and automatically casts the variable to the target type in the scope where the is or !is check is true.

val obj: Any = "The quick brown fox jumped over a lazy dog"
if(obj is String) {
    // The variable obj is automatically cast to a String in this scope.
    // No Explicit Casting needed. 
    println("Found a String of length ${obj.length}")
}

Similarly, for !is check –

val obj: Any = "The quick brown fox jumped over a lazy dog"
if(obj !is String) {
    println("Not a String")
} else {
    // obj is automatically cast to a String in this scope
    println("Found a String of length ${obj.length}")
}

That’s not all, Smart Casts also work with Short-Circuit operators && and || –

/* 
   obj is automatically cast to String on the right-hand side 
   of "&&" and in the "if" branch
*/
if(obj is String && obj.length > 0) {
    println("Found a String of length greater than zero - ${obj.length}")
}
// obj is automatically cast to String on the right-hand side of "||"
if(obj !is String || obj.length > 0) {
    return
}

Note that Smart Casts work only if the compiler can guarantee that the variable hasn’t changed after the is or !is check.

For example, Smart cast doesn’t work for mutable properties of a class. It works only for immutable properties that don’t have a custom getter.

Explicit Casting

1. Unsafe Cast Operator: as

You can use Kotlin’s Type Cast Operator as to manually cast a variable to a target type –

val obj: Any = "The quick brown fox jumped over a lazy dog"
val str: String = obj as String
println(str.length)

If the variable can’t be cast to the target type then the cast operator throws an exception. That’s why we call the as operator “Unsafe” –

val obj: Any = 123
val str: String = obj as String 
// Throws java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

Note that if the variable that you’re trying to cast is nullable then you can’t cast it to a non-null type –

val obj: Any? = null
val str: String = obj as String
// Throws kotlin.TypeCastException: null cannot be cast to non-null type kotlin.String

The target type needs to be nullable as well for the casting to work –

val obj: Any? = null
val str: String? = obj as String? // Works
println(str) // Prints null

2. Safe Cast operator: as?

As you learned in the previous section, the type cast operator as throws ClassCastException at runtime if the casting is not possible.

Kotlin also provides a Safe cast operator as? that returns null instead of throwing a ClassCastException if the casting is not possible –

val obj: Any = 123
val str: String? = obj as? String // Works
println(str)  // Prints null

Type Check & Smart Cast Example with User Defined Classes and Inheritance

The following example demonstrates Kotlin’s Type Check and Smart Cast concepts using User Defined Classes and inheritance –

open class Animal

class Cat : Animal() {
    fun meow() {
        println("Meow Meow Meow...")
    }
}

class Dog: Animal() {
    fun bark() {
        println("Woof Woof Woof...")
    }
}
fun main(args: Array<String>) {
    val animal: Animal = Cat()

    if(animal is Cat) {
    	// No explicit casting needed to `Cat`
        println(animal.meow())
    } else if (animal is Dog) {
    	// No explicit casting needed to `Dog`
        println(animal.bark())
    }
}
# Output
Meow Meow Meow...

Conclusion

That’s all folks! In this article, You learned how Type Checks and Smart Casts work in Kotlin. You also learned how to use Unsafe and Safe Type Cast Operators for explicitly casting a variable to a target type.

Kotlin Variables and Data Types
07
Mar
2021

Kotlin Variables and Data Types

In this article, You’ll learn how to declare variables in Kotlin, how Kotlin infers the type of variables, and what are the basic data types supported by Kotlin for creating variables.

You’ll also learn how to work with various data types and how to convert one type to another.

Variables

A variable refers to a memory location that stores some data. It has a name and an associated type. The type of a variable defines the range of values that the variable can hold, and the operations that can be done on those values.

You can declare a variable in Kotlin using var and val keywords.

A variable declared using val keyword is immutable (read-only). It cannot be reassigned after it is initialized –

val name = "Bill Gates"
name = "Satoshi Nakamoto"	// Error: Val cannot be reassigned 

For defining a mutable variable, i.e. a variable whose value can be changed, use the var keyword –

var country = "USA"
country = "India"    // Works

Type inference

Did you notice one thing about the variable declarations in the previous section? We didn’t specify the type of variables.

Although Kotlin is a statically typed language, It doesn’t require you to explicitly specify the type of every variable you declare. It can infer the type of a variable from the initializer expression –

val greeting = "Hello, World"  // type inferred as `String`
val year = 2018                // type inferred as `Int`

If you want to explicitly specify the type of a variable, you can do that like this –

// Explicitly defining the type of variables
val greeting: String = "Hello, World" 
val year: Int = 2018

Note that the type declaration becomes mandatory if you’re not initializing the variable at the time of declaration –

var language   // Error: The variable must either have a Type annotation or be initialized
language =  "French"

The above variable declaration fails because Kotlin has no way to infer the type of the variable without an initializer expression. In this case, you must explicitly specify the type of the variable –

var language: String   // Works
language = "French"

Data Types

Data Types are used to categorize a set of related values and define the operations that can be done on them.

Just like other languages, Kotlin has predefined types like IntDouble BooleanChar etc.

In Kotlin, everything (even the basic types like Int and Boolean) is an object. More specifically, everything behaves like an Object.

Kotlin may represent some of the basic types like numbers, characters and booleans as primitive values at runtime to improve performance, but for the end users, all of them are objects.

This is contrary to languages like Java that has separate primitive types like intdoubleetc, and their corresponding wrapper types like IntegerDouble etc.

Let’s now look at all the basic data types used in Kotlin one by one –

Numbers

Numeric types in Kotlin are similar to Java. They can be categorized into integer and floating point types.

Integers

  • Byte – 8 bit
  • Short– 16 bit
  • Int – 32 bit
  • Long – 64 bit

Floating Point Numbers

  • Float – 32 bit single-precision floating point value.
  • Double – 64 bit double-precision floating point value.

Following are few examples of numeric types –

// Kotlin Numeric Types Examples
val myByte: Byte = 10
val myShort: Short = 125

val myInt = 1000
val myLong = 1000L	// The suffix 'L' is used to specify a long value

val myFloat = 126.78f   // The suffix 'f' or 'F' represents a Float 
val myDouble = 325.49

You can also use underscore in numeric values to make them more readable –

val hundredThousand = 100_000
val oneMillion = 1_000_000

You can declare hexadecimal and binary values like this –

val myHexa = 0x0A0F  // Hexadecimal values are prefixed with '0x' or '0X'
val myBinary = 0b1010  // Binary values are prefixed with '0b' or '0B'

Note that Kotlin doesn’t have any representation for octal values.

Booleans

The type Boolean is used to represent logical values. It can have two possible values true and false.

val myBoolean = true
val anotherBoolean = false

Characters

Characters are represented using the type Char. Unlike Java, Char types cannot be treated as numbers. They are declared using single quotes like this –

val letterChar = 'A'
val digitChar = '9'

Just like other languages, special characters in Kotlin are escaped using a backslash. Some examples of escaped characters are – \n (newline), \t (tab), \r (carriage return), \b (backspace) etc.

Strings

Strings are represented using the String class. They are immutable, that means you cannot modify a String by changing some of its elements.

You can declare a String like this –

var myStr = "Hello, Kotlin"

You can access the character at a particular index in a String using str[index]. The index starts from zero –

var name = "John"
var firstCharInName = name[0]  // 'J'
var lastCharInName = name[name.length - 1]  // 'n'

The length property is used to get the length of a String.

Escaped String and Raw String

Strings declared in double quotes can have escaped characters like ‘\n’ (new line), ‘\t’ (tab) etc –

var myEscapedString = "Hello Reader,\nWelcome to my Blog"

In Kotlin, you also have an option to declare raw strings. These Strings have no escaping and can span multiple lines –

var myMultilineRawString = """
    The Quick Brown Fox
    Jumped Over a Lazy Dog.
"""

Arrays

Arrays in Kotlin are represented using the Array class. You can create an array in Kotlin either using the library function arrayOf() or using the Array() constructor.

Creating Arrays using the arrayOf library function

You can pass a bunch of values to the arrayOf function to create an array like this –

var numbers = arrayOf(1, 2, 3, 4, 5)
var animals = arrayOf("Cat", "Dog", "Lion", "Tiger")

Note that you can also pass values of mixed types to the arrayOf() function, and it will still work (but don’t do that) –

var mixedArray = arrayOf(1, true, 3, "Hello", 'A')	// Works and creates an array of Objects

You can also enforce a particular type while creating the array like this –

var numArray = arrayOf<Int>(1, 2, 3, 4, "Hello")  // Compiler Error

Accessing the elements of an array by their index

You can access the element at a particular index in an array using array[index]. The index starts from zero –

val myDoubleArray = arrayOf(4.0, 6.9, 1.7, 12.3, 5.4)
val firstElement = myDoubleArray[0]
val lastElement = myDoubleArray[myDoubleArray.size - 1]

Every array has a size property that you can use to get the size of the array.

You can also modify the array element at an index like this –

val a = arrayOf(4, 5, 7)  // [4, 5, 7]
a[1] = 10		          // [4, 10, 7]	

Primitive Arrays

As we learned earlier, everything in Kotlin is an object. But to improve performance it represents some of the basic types like numbers, characters and booleans as primitive types at runtime.

The arrayOf() function creates arrays of boxed/wrapper types. That is, arrayOf(1, 2, 3) corresponds to Java’s Integer[] array.

But, Kotlin provides a way to create arrays of primitive types as well. It contains specialized classes for representing array of primitive types. Those classes are – IntArrayDoubleArrayCharArray etc. You can create an array of primitive types using the corresponding library functions – intArrayOf()doubleArrayOf()charArrayOf() etc. –

val myCharArray = charArrayOf('K', 'O', 'T')  // CharArray (corresponds to Java 'char[]')
val myIntArray = intArrayOf(1, 3, 5, 7)		// IntArray (corresponds to Java 'int[]')

Creating Arrays using the Array() constructor

The Array() constructor takes two arguments –

  1. the size of the array, and
  2. a function that takes the array index as an argument and returns the element to be inserted at that index.
var mySquareArray = Array(5, {i -> i * i})	// [0, 1, 4, 9, 16]

The second argument to the Array() constructor is a lambda expression. Lambda expressions are anonymous functions that are declared and passed around as expressions. We’ll learn more about lambda expressions in a future tutorial.

The above lambda expression takes the index of an array element and returns the value that should be inserted at that index, which is the square of the index in this case.

Type Conversions

Unlike Java, Kotlin doesn’t support implicit conversion from smaller types to larger types. For example, Int cannot be assigned to Long or Double.

var myInt = 100
var myLong: Long = myInt // Compiler Error

However, Every number type contains helper functions that can be used to explicitly convert one type to another.

Following helper functions are supported for type conversion between numeric types –

  • toByte()
  • toShort()
  • toInt()
  • toLong()
  • toFLoat()
  • toDouble()
  • toChar()

Examples of explicit type conversions

Here is how you can convert an Int to Long –

val myInt = 100
val myLong = myInt.toLong()   // Explicitly converting 'Int' to 'Long'

You can also convert larger types to smaller types –

val doubleValue = 176.80
val intValue = doubleValue.toInt()  // 176

Every type in Kotlin, not just numeric type, supports a helper function called toString() to convert it to String.

val myInt = 1000
myInt.toString()  // "1000"

You can also convert a String to a numeric type like so –

val str = "1000"
val intValue = str.toInt()

If the String-to-Number conversion is not possible then a NumberFormatException is thrown –

val str = "1000ABC"
str.toInt()   // Throws java.lang.NumberFormatException

Conclusion

That’s all folks! In this article, You learned how to declare mutable and immutable variables. How type inference works in Kotlin. What are the basic data types supported in Kotlin. How to work with data types like IntLong DoubleCharBooleanString and Array, and how to convert one type to another.

How to read a File in Java
03
Mar
2021

How to read a File in Java

In this article, you’ll learn how to read a text file or binary (image) file in Java using various classes and utility methods provided by Java like:

BufferedReaderLineNumberReader
Files.readAllLinesFiles.lines
BufferedInputStreamFiles.readAllBytes, etc.

Let’s look at each of the different ways of reading a file in Java with the help of examples.

Java read file using BufferedReader

BufferedReader is a simple and performant way of reading text files in Java. It reads text from a character-input stream. It buffers characters to provide efficient reading.

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class BufferedReaderExample {
    public static void main(String[] args) {
        Path filePath = Paths.get("demo.txt");
        Charset charset = StandardCharsets.UTF_8;

        try (BufferedReader bufferedReader = Files.newBufferedReader(filePath, charset)) {
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException ex) {
            System.out.format("I/O error: %s%n", ex);
        }
    }
}

Java read file line by line using Files.readAllLines()

Files.readAllLines() is a utility method of the Java NIO’s Files class that reads all the lines of a file and returns a List<String> containing each line. It internally uses BufferedReader to read the file.

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class FilesReadAllLinesExample {
    public static void main(String[] args) {
        Path filePath = Paths.get("demo.txt");
        Charset charset = StandardCharsets.UTF_8;
        try {
            List<String> lines = Files.readAllLines(filePath, charset);
            for(String line: lines) {
                System.out.println(line);
            }
        } catch (IOException ex) {
            System.out.format("I/O error: %s%n", ex);
        }
    }
}

Java read file line by line using Files.lines()

Files.lines() method reads all the lines from a file as a Stream. You can use Stream API methods like forEachmap to work with each line of the file.

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FilesLinesExample {
    public static void main(String[] args) {
        Path filePath = Paths.get("demo.txt");
        Charset charset = StandardCharsets.UTF_8;

        try {

            Files.lines(filePath, charset)
                    .forEach(System.out::println);

        } catch (IOException ex) {
            System.out.format("I/O error: %s%n", ex);
        }
    }
}

Java read file line by line using LineNumberReader

LineNumberReader is a buffered character-stream reader that keeps track of line numbers. You can use this class to read a text file line by line.

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class LineNumberReaderExample {
    public static void main(String[] args) {
        Path filePath = Paths.get("demo.txt");
        Charset charset = StandardCharsets.UTF_8;

        try(BufferedReader bufferedReader = Files.newBufferedReader(filePath, charset);
            LineNumberReader lineNumberReader = new LineNumberReader(bufferedReader)) {

            String line;
            while ((line = lineNumberReader.readLine()) != null) {
                System.out.format("Line %d: %s%n", lineNumberReader.getLineNumber(), line);
            }
        } catch (IOException ex) {
            System.out.format("I/O error: %s%n", ex);
        }
    }
}

Java read binary file (image file) using BufferedInputStream

All the examples presented in this article so far read textual data from a character-input stream. If you’re reading a binary data such as an image file then you need to use a byte-input stream.

BufferedInputStream lets you read raw stream of bytes. It also buffers the input for improving performance

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;

public class BufferedInputStreamImageCopyExample {
    public static void main(String[] args) {
        try(InputStream inputStream = Files.newInputStream(Paths.get("sample.jpg"));
            BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);

            OutputStream outputStream = Files.newOutputStream(Paths.get("sample-copy.jpg"));
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) {

            byte[] buffer = new byte[4096];
            int numBytes;
            while ((numBytes = bufferedInputStream.read(buffer)) != -1) {
                bufferedOutputStream.write(buffer, 0, numBytes);
            }
        } catch (IOException ex) {
            System.out.format("I/O error: %s%n", ex);
        }
    }
}

Java read file into []byte using Files.readAllBytes()

If you want to read the entire contents of a file in a byte array then you can use the Files.readAllBytes() method.

import com.sun.org.apache.xpath.internal.operations.String;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class FilesReadAllBytesExample {
    public static void main(String[] args) {
        try {
            byte[] data = Files.readAllBytes(Paths.get("demo.txt"));

            // Use byte data
        } catch (IOException ex) {
            System.out.format("I/O error: %s%n", ex);
        }

    }
}
Microservices Architecture
14
May
2021

Microservices Architecture

Microservices are defined as a self-regulating, and independent codebase that can be written and maintained even by a small team of developers. Microservices Architecture consists of such loosely coupled services with each service responsible for the execution of its associated business logic. 

The services are separated from each other based on the nature of their domains and belong to a mini-microservice pool. Enterprise mobile app developers leverage the capabilities of this architecture especially for complex applications. 

Microservices Architecture allows developers to release versions of software thanks to sophisticated automation of software building, testing, and deployment – something that acts as a prime differentiation point between Microservices and Monolithic architecture.

Microservices Architecture

Benefits

  • Since the services are bifurcated into pools, the architecture design pattern makes the system highly fault-tolerant. In other words, the whole software won’t collapse on its head even if some microservices cease to function. 
  • An enterprise mobile app development company working on such an architecture for clients can deploy multiple programming languages to build different microservices for their specific purpose. Therefore the technology stack can be kept updated with the latest upgrades in computing. 
  • This architecture is a perfect fit for applications that need to scale. Since the services are already independent of each other, they can scale individually rather than overloading the entire system with the need to expand. 
  • Services can be integrated into any application depending upon the scope of work. 

Potential Drawbacks 

  • Since each service is unique in its ability to contribute to the whole codebase, it could be challenging for an enterprise mobile application development company to interlink all and operate so many distinctive services seamlessly. 
  • Developers must define a standard protocol for all services to adhere to. It is important to do so, as the decentralized approach towards coding microservices in multiple languages can pose serious issues while debugging. 
  • Each microservice with its limited environment is responsible to maintain the integrity of the data. It is up to the architects of such a system to come up with a universally consistent data integrity protocol, wherever possible. 
  • You definitely need the best of breed professionals to design such a system for you as the technology stack keeps changing. 

Ideal For

Use Microservices Architecture for apps in which a specific segment will be used heavily than the others and would need a sporadic burst of scaling. Instead of a standalone application you may also deploy this for a service that provides functionality to other applications of the system. 

Micro-frontends: The path to a scalable future — part 2
26
Mar
2021

Micro-frontends: The path to a scalable future — part 2

Introduction

So in Part 1 of this article, we’ve learned what micro-frontends are, what they are meant for, and what the main concepts associated with them are. In this part, we are going to dive deep into various micro-frontend architecture types. We will understand what characteristics they share and what they don’t. We’ll see how complex each architecture is to implement. Then through the documents-to-applications continuum concept, we will identify our application type and choose the proper micro-frontend architecture that meets our application’s needs at most.

Micro-frontend architecture characteristics

In order to define micro-frontend architecture types, let’s reiterate over two micro-frontend integration concepts that we learned in the first part: (Fig. 1 and 2).

  • routing/page transitions

For page transition, we’ve seen that there are server and client-side routings, which in another way are called hard and soft transitions. It’s “hard” because with server-side routing, each page transition leads to a full page reload, which means for a moment the user sees a blank page until the content starts to render and show up. And client-side routing is called soft because on page transition, only some part of the page content is being reloaded (usually header, footer, and some common content stays as it is) by JavaScript. So there is no blank page, and, also the new content is smaller so it arrives earlier than during the full page reload. Therefore, with soft transitions, we undoubtedly have a better user experience than with hard ones.

Figure 1. routing/page transition types

Currently, almost all modern web applications are Single Page Applications (SPAs). This is a type of application where all page transitions are soft and smooth. When speaking about micro-frontends, we can consider each micro-frontend as a separate SPA, so the page transitions within each of them can also be considered soft. From a micro-frontend architectural perspective, by saying page transitions, we meant the transitions from one micro frontend to another and not the local transitions within a micro frontend. Therefore this transition type is one of the key characteristics of a micro-frontend architecture type.

  • composition/rendering

For composition, we’ve spoken about the server and client-side renderings. In the 2000s, when JavaScript was very poor and limited, almost all websites were fully rendered on the server-side. With the evolution of web applications and JavaScript, client-side rendering became more and more popular. SPAs mentioned earlier also are applications with client-side rendering.

Figure 2. composition/rendering types

Currently, when saying server-side rendering or universal rendering, we don’t assume that the whole application is rendered on the server-side — it’s more like hybrid rendering where some content is rendered on the server-side and the other part on the client-side. In the first article about micro-frontends, we’ve spoken about the cases when universal rendering can be useful. So the rendering is the second characteristic of a micro-frontend architecture type.

Architecture types

Now that we have defined two characteristics of micro-frontend architecture, we can extract four types based on various combinations of those two.

Linked Applications

  • Hard transitions;
  • Client-side rendering;

This is the simplest case where the content rendering is being done on the client-side and where the transition between micro-frontends is hard. As an example, we can consider two SPAs that are hosted under the same domain (http://team.com). A simple Nginx proxy server serves the first micro-frontend on the path “/a” and the second micro-frontend on the path “/b.” Page transitions within each micro-frontend are smooth as we assumed they are SPAs.

The transition from one micro-frontend to another happens via links which in fact is a hard transition and leads to a full page reload (Fig. 3). This micro-frontend architecture has the simplest implementation complexity.

Figure 3. Linked application transition model

Linked Universal Applications

  • Hard transitions;
  • Universal rendering;

If we add universal rendering features to the previous case, we’ll get the Linked Universal Applications architecture type. This means that some content of either one or many micro frontends renders on the server-side. But still, the transition from one micro to another is done via links. This architecture type is more complex than the first one as we have to implement server-side rendering.

Unified Applications

  • Soft transitions;
  • Client-side rendering;

Here comes my favorite part. To have a soft transition between micro-frontends, there should be a shell-application that will act as an umbrella for all micro-frontend applications. It will only have a markup layout and, most importantly, a top-level client routing. So the transition from path “/a” to path “/b” will be handed over to the shell-application (Fig. 4). As it is a client-side routing and is the responsibility of JavaScript, it can be done softly, making a perfect user experience. Though this type of application is fully rendered on the client-side, there is no universal rendering of this architecture type.

Figure 4. Unified application with a shell application

Unified Universal Applications

  • Soft transitions;
  • Universal rendering;

And here we reach the most powerful and, at the same time, the most complex architecture type where we have both soft transitions between micro-frontends and universal rendering (Fig. 5).

Figure 5. Micro-frontend architecture types

A logical question can arise like, “oh, why don’t we pick the fourth and the best option?” Simply because these four architecture types have different levels of complexity, and the more complex our architecture is, the more we will pay for it for both implementation and maintenance (Fig. 6). So there is no absolute right or wrong architecture. We just have to find a way to understand how to choose the right architecture for our application, and we are going to do it in the next section.

Figure 6. Micro-frontend architecture types based on their implementation complexities.

Which architecture to choose?

There is an interesting concept called the documents-to-applications continuum (Fig. 7). It’s an axis with two ends where we have more content-centric websites, and on the other side, we have pure web applications that are behavior-centric.

  • Content-centric: the priority is given to the content. Think of some news or a blog website where users usually open an article. The content is what matters in such websites; the faster it loads, the more time the users save. There are not so many transitions in these websites, so they can easily be hard, and no one will care.
  • Behavior-centric: the priority is given to the user experience. These are pure applications like online code editors, and playgrounds, etc. In these applications, you are usually in a flow of multiple actions. Thus there are many route/state transitions and user interactions along with the flow. For such types of applications having soft transitions is key for good UX.
Figure 7. Correlation of the document-to-applications continuum and micro-frontend architecture types.

From Fig. 7, we can see that there is an overlap of content and behavior-centric triangles. This is the area where the progressive web applications lay. These are applications where both the content and the user experience are important. A good example is an e-commerce web application.

The faster the first page loads, the more pleasant it will be for the customers to stay on the website and shop.

The first example is that of a content-centric application, while the second one is behavior-centric.

Now let’s see where our architecture types lay in the document-to-applications continuum. Since we initially assumed that each micro-frontend is a SPA by itself, we can assume that all our architecture types lay in the behavior-centric spectrum. Note that this assumption of SPA’s is not a prerogative and is more of an opinionated approach. As both linked universal apps and universal unified apps have universal rendering features, they will lay in progressive web apps combined with both content and behavior-centric spectrums. Linked apps and unified apps are architecture types that are more suited to web applications where the best user experience has higher priority than the content load time.

First of all, we have to identify our application requirements and where it is positioned in the document-to-application continuum. Then it’s all about what complexity level of implementation we can afford.

Conclusion

This time, as we already learned the main concepts of micro-frontends, we dived deep into micro-frontend applications’ high-level architecture. We defined architecture characteristics and came up with four types. And at the end, we learned how and based on what to choose our architecture type. I’d like to mention that I’ve gained most of the knowledge on micro-frontends and especially the things I wrote about in this article from the book by Micheal Geers “ Micro frontends in Action,” which I recommend reading if you want to know much more about this topic.

What’s next?

For the next chapter, we will be going towards the third architecture — Unified apps, as this type of architecture, mostly fits the AI annotation platform. We will be learning how to build unified micro-frontends. So stay tuned for the last third part.

JPA / Hibernate One to Many Mapping Example with Spring Boot
03
Mar
2021

JPA / Hibernate One to Many Mapping Example with Spring Boot

In this article, you’ll learn how to map a one-to-many database relationship at the object level using JPA and Hibernate.

Consider the following two tables – posts and comments of a Blog database schema where the posts table has a one-to-many relationship with the comments table –

JPA / Hibernate One to Many Mapping Example Table Structure

We’ll create a project from scratch and learn how to go about implementing such one-to-many relationship at the object level using JPA and hibernate.

We’ll also write REST APIs to perform CRUD operations on the entities so that you fully understand how to actually use these relationships in the real world.

Creating the Project

If you have Spring Boot CLI installed, then you can type the following command in your terminal to generate the project –

spring init -n=jpa-one-to-many-demo -d=web,jpa,mysql --package-name=com.example.jpa jpa-one-to-many-demo

Alternatively, You can generate the project from Spring Initializr web tool by following the instructions below –

  1. Go to http://start.spring.io
  2. Enter Artifact as “jpa-one-to-many-demo”
  3. Click Options dropdown to see all the options related to project metadata.
  4. Change Package Name to “com.example.jpa”
  5. Select WebJPA and Mysql dependencies.
  6. Click Generate to download the project.

Following is the directory structure of the project for your reference –

JPA, Hibernate, Spring Boot One to Many Mapping example Directory Structure

Your bootstrapped project won’t have modelcontrollerrepository and exception packages, and all the classes inside these packages at this point. We’ll create them shortly.

Configuring the Database and Logging

Since we’re using MySQL as our database, we need to configure the database URL, username, and password so that Spring can establish a connection with the database on startup. Open src/main/resources/application.properties file and add the following properties to it –

# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url=jdbc:mysql://localhost:3306/jpa_one_to_many_demo?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username=root
spring.datasource.password=root

# Hibernate

# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type=TRACE

Don’t forget to change the spring.datasource.username and spring.datasource.password as per your MySQL installation. Also, create a database named jpa_one_to_many_demo in MySQL before proceeding to the next section.

You don’t need to create any tables. The tables will automatically be created by hibernate from the Post and Comment entities that we will define shortly. This is made possible by the property spring.jpa.hibernate.ddl-auto = update.

We have also specified the log levels for hibernate so that we can debug all the SQL statements and learn what hibernate does under the hood.

The best way to model a one-to-many relationship in hibernate

I have been working with hibernate for quite some time and I’ve realized that the best way to model a one-to-many relationship is to use just @ManyToOne annotation on the child entity.

The second best way is to define a bidirectional association with a @OneToMany annotation on the parent side of the relationship and a @ManyToOne annotation on the child side of the relationship. The bidirectional mapping has its pros and cons. I’ll demonstrate these pros and cons in the second section of this article. I’ll also tell you when a bidirectional mapping is a good fit.

But let’s first model our one-to-many relationship in the best way possible.

Defining the Domain Models

In this section, we’ll define the domain models of our application – Post and Comment.

Note that both Post and Comment entities contain some common auditing related fields like created_at and updated_at.

We’ll abstract out these common fields in a separate class called AuditModel and extend this class in the Post and Comment entities.

We’ll also use Spring Boot’s JPA Auditing feature to automatically populate the created_at and updated_at fields while persisting the entities.

1. AuditModel

package com.example.jpa.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Date;

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(
        value = {"createdAt", "updatedAt"},
        allowGetters = true
)
public abstract class AuditModel implements Serializable {
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created_at", nullable = false, updatable = false)
    @CreatedDate
    private Date createdAt;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "updated_at", nullable = false)
    @LastModifiedDate
    private Date updatedAt;

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    public Date getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Date updatedAt) {
        this.updatedAt = updatedAt;
    }
}

In the above class, we’re using Spring Boot’s AuditingEntityListener to automatically populate the createdAt and updatedAt fields.

Enabling JPA Auditing

To enable JPA Auditing, you’ll need to add @EnableJpaAuditing annotation to one of your configuration classes. Open the main class JpaOneToManyDemoApplication.java and add the @EnableJpaAuditing to the main class like so –

package com.example.jpa;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
public class JpaOneToManyDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(JpaOneToManyDemoApplication.class, args);
    }
}

2. Post model

package com.example.jpa.model;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Entity
@Table(name = "posts")
public class Post extends AuditModel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Size(max = 100)
    @Column(unique = true)
    private String title;

    @NotNull
    @Size(max = 250)
    private String description;

    @NotNull
    @Lob
    private String content;

    // Getters and Setters (Omitted for brevity)
}

3. Comment model

package com.example.jpa.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;

@Entity
@Table(name = "comments")
public class Comment extends AuditModel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Lob
    private String text;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "post_id", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    @JsonIgnore
    private Post post;

    // Getters and Setters (Omitted for brevity)
}

The Comment model contains the @ManyToOne annotation to declare that it has a many-to-one relationship with the Post entity. It also uses the @JoinColumn annotation to declare the foreign key column.

Defining the Repositories

Next, We’ll define the repositories for accessing the data from the database. Create a new package called repository inside com.example.jpa package and add the following interfaces inside the repository package –

1. PostRepository

package com.example.jpa.repository;

import com.example.jpa.model.Post;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {

}

2. CommentRepository

package com.example.jpa.repository;

import com.example.jpa.model.Comment;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface CommentRepository extends JpaRepository<Comment, Long> {
    Page<Comment> findByPostId(Long postId, Pageable pageable);
    Optional<Comment> findByIdAndPostId(Long id, Long postId);
}

Writing the REST APIs to perform CRUD operations on the entities

Let’s now write the REST APIs to perform CRUD operations on Post and Comment entities.

All the following controller classes are define inside com.example.jpa.controller package.

1. PostController (APIs to create, retrieve, update, and delete Posts)

package com.example.jpa.controller;

import com.example.jpa.exception.ResourceNotFoundException;
import com.example.jpa.model.Post;
import com.example.jpa.repository.PostRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@RestController
public class PostController {

    @Autowired
    private PostRepository postRepository;

    @GetMapping("/posts")
    public Page<Post> getAllPosts(Pageable pageable) {
        return postRepository.findAll(pageable);
    }

    @PostMapping("/posts")
    public Post createPost(@Valid @RequestBody Post post) {
        return postRepository.save(post);
    }

    @PutMapping("/posts/{postId}")
    public Post updatePost(@PathVariable Long postId, @Valid @RequestBody Post postRequest) {
        return postRepository.findById(postId).map(post -> {
            post.setTitle(postRequest.getTitle());
            post.setDescription(postRequest.getDescription());
            post.setContent(postRequest.getContent());
            return postRepository.save(post);
        }).orElseThrow(() -> new ResourceNotFoundException("PostId " + postId + " not found"));
    }


    @DeleteMapping("/posts/{postId}")
    public ResponseEntity<?> deletePost(@PathVariable Long postId) {
        return postRepository.findById(postId).map(post -> {
            postRepository.delete(post);
            return ResponseEntity.ok().build();
        }).orElseThrow(() -> new ResourceNotFoundException("PostId " + postId + " not found"));
    }

}

2. CommentController (APIs to create, retrieve, update, and delete Comments)

package com.example.jpa.controller;

import com.example.jpa.exception.ResourceNotFoundException;
import com.example.jpa.model.Comment;
import com.example.jpa.repository.CommentRepository;
import com.example.jpa.repository.PostRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@RestController
public class CommentController {

    @Autowired
    private CommentRepository commentRepository;

    @Autowired
    private PostRepository postRepository;

    @GetMapping("/posts/{postId}/comments")
    public Page<Comment> getAllCommentsByPostId(@PathVariable (value = "postId") Long postId,
                                                Pageable pageable) {
        return commentRepository.findByPostId(postId, pageable);
    }

    @PostMapping("/posts/{postId}/comments")
    public Comment createComment(@PathVariable (value = "postId") Long postId,
                                 @Valid @RequestBody Comment comment) {
        return postRepository.findById(postId).map(post -> {
            comment.setPost(post);
            return commentRepository.save(comment);
        }).orElseThrow(() -> new ResourceNotFoundException("PostId " + postId + " not found"));
    }

    @PutMapping("/posts/{postId}/comments/{commentId}")
    public Comment updateComment(@PathVariable (value = "postId") Long postId,
                                 @PathVariable (value = "commentId") Long commentId,
                                 @Valid @RequestBody Comment commentRequest) {
        if(!postRepository.existsById(postId)) {
            throw new ResourceNotFoundException("PostId " + postId + " not found");
        }

        return commentRepository.findById(commentId).map(comment -> {
            comment.setText(commentRequest.getText());
            return commentRepository.save(comment);
        }).orElseThrow(() -> new ResourceNotFoundException("CommentId " + commentId + "not found"));
    }

    @DeleteMapping("/posts/{postId}/comments/{commentId}")
    public ResponseEntity<?> deleteComment(@PathVariable (value = "postId") Long postId,
                              @PathVariable (value = "commentId") Long commentId) {
        return commentRepository.findByIdAndPostId(commentId, postId).map(comment -> {
            commentRepository.delete(comment);
            return ResponseEntity.ok().build();
        }).orElseThrow(() -> new ResourceNotFoundException("Comment not found with id " + commentId + " and postId " + postId));
    }
}

The ResourceNotFoundException Class

Both the Post and Comment Rest APIs throw ResourceNotFoundException when a post or comment could not be found. Following is the definition of the ResourceNotFoundException.

package com.example.jpa.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException() {
        super();
    }

    public ResourceNotFoundException(String message) {
        super(message);
    }

    public ResourceNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}

I have added the @ResponseStatus(HttpStatus.NOT_FOUND) annotation to the above exception class to tell Spring Boot to respond with a 404 status when this exception is thrown.

Running the Application and Testing the APIs via a Postman

You can run the application by typing the following command in the terminal –

mvn spring-boot:run

Let’s now test the APIs via Postman.

Create Post POST /posts

Spring Boot Hibernate Jpa One to Many Mapping with Pagination and Rest API

Get paginated Posts GET /posts?page=0&size=2&sort=createdAt,desc

Spring Boot Hibernate Jpa One to Many Mapping with Pagination and Sorting Rest API

Create Comment POST /posts/{postId}/comments

Spring Boot Hibernate Jpa One to Many Mapping with Pagination and Sorting Rest API create comments

Get paginated comments GET /posts/{postId}/comments?page=0&size=3&sort=createdAt,desc

Spring Boot Hibernate Jpa One to Many Mapping with Pagination and Sorting Rest API get comments

You can test other APIs in the same way.

How to define a bidirectional one-to-many mapping and when should you use it

The Internet is flooded with examples of bidirectional one-to-many mapping. But it’s not the best and the most efficient way to model a one-to-many relationship.

Here is the bidirectional version of the one-to-many relationship between the Post and Comment entities –

Post Entity

package com.example.jpa.model;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.*;

@Entity
@Table(name = "posts")
public class Post extends AuditModel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Size(max = 100)
    @Column(unique = true)
    private String title;

    @NotNull
    @Size(max = 250)
    private String description;

    @NotNull
    @Lob
    private String content;

    @OneToMany(cascade = CascadeType.ALL,
            fetch = FetchType.LAZY,
            mappedBy = "post")
    private Set<Comment> comments = new HashSet<>();

    // Getters and Setters (Omitted for brevity)
}

Comment Entity

package com.example.jpa.model;

import javax.persistence.*;
import javax.validation.constraints.NotNull;

@Entity
@Table(name = "comments")
public class Comment extends AuditModel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Lob
    private String text;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "post_id", nullable = false)
    private Post post;

    // Getters and Setters (Omitted for brevity)
}

The idea with bidirectional one-to-many association is to allow you to keep a collection of child entities in the parent, and enable you to persist and retrieve the child entities via the parent entity. This is made possible via Hibernate’s entity state transitions and dirty checking mechanism.

For example, here is how you could persist comments via post entity in the bidirectional mapping –

// Create a Post
Post post = new Post("post title", "post description", "post content");

// Create Comments
Comment comment1 = new Comment("Great Post!");
comment1.setPost(post);
Comment comment2 = new Comment("Really helpful Post. Thanks a lot!");
comment2.setPost(post);

// Add comments in the Post
post.getComments().add(comment1);
post.getComments().add(comment2);

// Save Post and Comments via the Post entity
postRepository.save(post);

Hibernate automatically issues insert statements and saves the comments added to the post.

Similarly, you could fetch comments via the post entity like so –

// Retrieve Post
Post post = postRepository.findById(postId)

// Get all the comments
Set<Comment> comments = post.getComments()

When you write post.getComments(), hibernate loads all the comments from the database if they are not already loaded.

Problems with bidirectional one-to-many mapping

  • A bidirectional mapping tightly couples the many-side of the relationship to the one-side.
  • In our example, If you load comments via the post entity, you won’t be able to limit the number of comments loaded. That essentially means that you won’t be able to paginate.
  • If you load comments via the post entity, you won’t be able to sort them based on different properties. You can define a default sorting order using @OrderColumn annotation but that will have performance implications.
  • You’ll find yourself banging your head around something called a LazyInitializationException.

When can I use a bidirectional one-to-many mapping

A bidirectional one-to-many mapping might be a good idea if the number of child entities is limited.

Moreover, A bidirectional mapping tightly couples the many-side of the relationship to the one-side. Many times, this tight coupling is desired.

For example, Consider a Survey application with a Question and an Option entity exhibiting a one-to-many relationship between each other.

In the survey app, A Question can have a set of Options. Also, every Question is tightly coupled with its Options. When you create a Question, you’ll also provide a set of Options. And, when you retrieve a Question, you will also need to fetch the Options.

Moreover, A Question can have at max 4 or 5 Options. These kind of cases are perfect for bi-directional mappings.

So, To decide between bidirectional and unidirectional mappings, you should think whether the entities have a tight coupling or not.

Conclusion

That’s all folks! In this article, You learned how to map a one-to-many database relationship using JPA and Hibernate.

More Resources

You may wanna check out the following articles by Vlad Mihalcea to learn more about Hibernate and it’s performance –

Thanks for reading. I hope you found the content useful. Consider subscribing to my newsletter if you don’t wanna miss future articles from The Fusebes Blog.

How to delete a directory recursively with all its subdirectories and files in Java
03
Mar
2021

How to delete a directory recursively with all its subdirectories and files in Java

In this short article, you’ll learn how to delete a directory recursively along with all its subdirectories and files.

There are two examples that demonstrate how to achieve this task. The idea behind both of the examples is to traverse the file tree, and delete the files in any directory before deleting the directory itself.

Delete directory recursively – Java 8+

This example makes use of Files.walk(Path) method that returns a Stream<Path> populated with Path objects by walking the file-tree in depth-first order.

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Comparator;

public class DeleteDirectoryRecursively {
    public static void main(String[] args) throws IOException {
        Path dir = Paths.get("java");

        // Traverse the file tree in depth-first fashion and delete each file/directory.
        Files.walk(dir)
                .sorted(Comparator.reverseOrder())
                .forEach(path -> {
                    try {
                        System.out.println("Deleting: " + path);
                        Files.delete(path);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
    }
}

Delete directory recursively – Java 7

The following example uses Files.walkFileTree(Path, FileVisitor) method that traverses a file tree and invokes the supplied FileVisitor for each file.

We use a SimpleFileVisitor to perform the delete operation.

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;

public class DeleteDirectoryRecursively1 {
    public static void main(String[] args) throws IOException {
        Path dir = Paths.get("java");

        // Traverse the file tree and delete each file/directory.
        Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                System.out.println("Deleting file: " + file);
                Files.delete(file);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                System.out.println("Deleting dir: " + dir);
                if (exc == null) {
                    Files.delete(dir);
                    return FileVisitResult.CONTINUE;
                } else {
                    throw exc;
                }
            }
        });
    }
}